-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Particular case of mutually recursive function not working #434
Comments
Since this also happens on the Lean alternative implementation, we believe that this is a limitation of the expected behavior rather than a real bug. So we should address this issue later, with a more robust strategy to deal with a wider range of ways to express recursions. For now, the rule of thumb is to define recursive calls as straightforward binds of symbols to lambdas in the |
Although I'm sure it's not exactly the same, something about this reminds me of #3 (lol, number three). The idea of an env becoming 'lost' may be conceptually useful as we work through this. |
I think I know the issue. When we find a function in a recursive environment, then we extend its closure-environment at the time it is found. That is how we provide recursion currently. What's happening in the bad example, is that the binding to This is just an explanation which more clearly explains why the 'rule of thumb' @arthurpaulino describe is actually what should be expected based on an understanding of how Lurk's |
In other words, the hypothesis that In fact, on consideration, this behavior is already documented in our tests. This test exists exactly to show the expected behavior (and happens to use the same examples…): https://github.com/lurk-lab/lurk-rs/blob/8fd97899fbf7776de650059d850d63ef7aacb1c5/src/proof/nova.rs#L1767-L1788 The only difference is that the example code uses the 'concise' form:
whereas the example in this issue manually expands it (as evaluation itself does) to
I think most of the confusion here comes because we're not used to reading code that avoids the concise form, so didn't recognize that this is a direct replication of the explanatory test case. Another way to phrase the final response here is, "Yes, this issue demonstrates that Lurk's letrec still does not provide mutual recursion." However, the more constructive observation is that there is indeed a clever way to obtain mutual recursion from the more limited self-recursion Lurk's letrec provides. This means we could actually implement mutual recursion as a macro ( Interesting! |
In other words, the interesting point about the original issue was not actually, "Mutual recursion is surprisingly broken in some case," which we always knew. But rather it is, "But Lurk is perfectly capable of expressing mutual recursion if we transform concise letrecs differently." Nice find, @gabriel-barrett. |
Correcting myself slightly: It's not true that the first (bad) example is exactly the same as the test demonstrating the error. That would actually expand to
Nevertheless, the general point remains. There are many ways to misuse Lurk's focused self-recursion construct ( Since accomplishing this does require that all bindings be to An example like the original could then be written:
And it would then become
This example does highlight one annoying point about the idea. In order to make all of the bound functions available in the body, we would need to separately define each entry point. If it's okay to have only a single entry point, then that's not necessary. However, I think we can probably come up with a way to build on this idea and also obtain all the bindings in one pass, probably by introducing new continuation types. |
Alternately, maybe it will be better to pursue one of the other strategies. I think we can get the properties we want by making the recursive lookups smarter. This will still require some tweaks to the continuations, but basically, when searching a recursive env, we can hold on to the enclosing recursive env as we descend, then use that as the env with which to extend the closure. In light of the above expansion, I now slightly lean toward that approach. |
In that model (the one involving making recursive lookups smarter), we wouldn't even need a new construct. Then this
would 'just work'. The rule would be that all bindings within a single 'concise' letrec would have all other bindings available within the bodies of any functions directly defined as the value of those bindings. I think this is exactly what we want to express, and a change to the lookup and function-env-extension mechanism seems like the simplest and most direct way to effect it. |
For some reason the following code does not work
However, adding the
letrec
foreven?
inside the lambda works:The text was updated successfully, but these errors were encountered: