-
Notifications
You must be signed in to change notification settings - Fork 52
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
Error when wrapping build #150
Comments
After some experimenting I noticed that this seems to be the funtamental error:
This gives a "Bad application..." error. |
Ah, thanks for reporting this. It turns out it's because of the way we rewrite applications of Wrapping the program argument to
It is
it is the base that will need arithmetic, because it will evaluate the (non-delayed) let-expression to the value I think the solution is to just allow The fact that the arguments show up in reverse order in the error message is a red herring --- it's an unrelated bug. |
@polux mentioned in IRC that this seems similar to "multi-stage programming", a la Meta OCaml. We could imagine In a sense, we have a very poor man's version of this already, with exactly two stages: evaluation and execution. Evaluating something of type |
Hmm, maybe a better solution would be to reflect the delay in the types. The simple way to do this would be to give We already have syntax for introducing delay and a |
I don't think that would solve the problem you described above.
In that case it's still the base who needs arithmetic.
Right! Having something like this instead of the full power of quasi-quotation may be enough. If I understand correctly, your proposed special brackets are akin to the "lazy" keyword of https://ocaml.org/api/Lazy.html, which must be weaker than Meta OCaml in ways I'm not sure I know how to completely characterize. I guess the key difference is splicing, which we may not need here indeed and adds a great deal of complexity. I'm still wondering if there aren't examples where the full power of staged programming wouldn't be needed to finely control who evaluates what, but I can't think of any right now. It would definitely be more user friendly if we could get away with the simpler mechanism. |
Well, if you explicitly make a non-lazy version of
Yes, that looks like exactly the same thing as what I propose.
Perhaps, but I think you can pretty easily control who evaluates what just by moving |
After I left that sink in and in the wake of #223 I realized a few things:
Regardless of whether or not we keep What do you think? |
@polux I was thinking about using |
First of all, I am definitely convinced re: using What do you think about punning on the
Correct. So I think you're right that
|
Great! Looks like we all agree on using
I think I like it:
If you're up for it then yes by all means! I agree that this is a prerequisite for any serious use of lazy data structures. The only thing that makes me a bit nervous is the cost of garbage collection. Please note that I haven't given much thought to whether a CESK machine is really needed for implementing that memoization. That is a hunch I have and you seem to share it but maybe there is a clever way to do that with a CEK machine? |
Good point. In fact I think at one point there was a discussion in IRC of using prefix
Agreed, I do not intend to use
Yes, it's worth thinking about this carefully, and we could quite possibly just leave garbage collection out entirely in a first pass. Probably in most cases no garbage collection will be needed since robots will allocate a small, constant number of memory cells (i.e. one memory cell per recursive definition).
I really don't think so. CEK machine environments are lexically scoped, but the store for memoized lazy values has to be dynamically scoped, because values can be returned out of the scope in which they started life. ( |
SGTM on everything you wrote! |
So I think we have another choice to make. In a previous comment I said
However, as I have been working on implementing this, I realize that if we implement this in the most straightforward way, that will not be true at all. The straightforward idea would be that evaluating a delayed expression would always allocate a memory cell to hold that expression, and later to hold whatever value it evaluates to once it is forced. However, if we now have things like So I'd like to propose something slightly more nuanced. We can have two different kinds of delayed expressions, memoized and non-memoized. Both will have the same type
|
As discussed on IRC this week-end, as much as I find this disappointing that we need to expose this difference to the user, I agree this is the most reasonable thing to do, for now at least. Once/if we implement garbage collection, we could investigate the hit taken by storing every delayed value in the store. |
I briefly thought that having a syntax for explicitly requesting memoization was unnecessary, since any place it would be needed would already use recursion or a definition anyway. But this is actually not true. As a counterexample, consider this future pseudo-swarm code:
Although the expression In IRC, @fryguybob mentioned the possibility of using weak references. The idea, as I understand it, would be to implement
But again, I'm not sure we can/should implement all this right away. |
I like the idea of using weak refs very much but I have a hard time imagining how this would work exactly. Would we still need the S in CESK? Or would it be like having IORefs in our machine state? What makes it a bit scary is that the machine state is not some inhabitant of an algebraic datatype that we can easily inspect/traverse/serialize etc. But I agree that co-opting the GC is appealing and may make it worth. |
We definitely still need to be able to serialize the machine state for #50 ! Here's how I imagine it would work. We still need the S in CESK, which would be a map from keys to There are a bunch of details to be worked out here which I don't quite understand, but intuitively I believe it should be possible. |
I suppose having |
What about just making |
The original intent of weak references in Haskell was to support fancy memoization. It may be helpful to read that paper: https://www.microsoft.com/en-us/research/wp-content/uploads/1999/09/stretching.pdf |
After some discussion on IRC, we think just having One nice idea would be to abstract over both the |
Make explicit in the type system when evaluation of a computation should be delayed. This gives the user fine-grained control over selective laziness (for example, once we have sum types and recursive types, one could use this to define lazy infinite data structures). It also allows us to guarantee that certain commands such as `build` and `reprogram` delay evaluation of their arguments, and lets the user e.g. define their own modified versions of `build` without compromising those guarantees. - Delay is indicated by curly braces both at the value and type levels, that is, if `t : ty` then `{t} : {ty}`. - `force : {ty} -> ty` is now exposed in the surface language. - Change from a CEK machine to a CESK machine. Recursive `let` and `def` delay via allocating a cell in the store. For now, there is no other way to allocate anything in the store, but see discussion at #150 for some possible future directions. - change the types of `build` and `reprogram` to require a delayed program, e.g. `build : string -> {cmd a} -> cmd string` - `if` and `try` also require delayed arguments. - don't elaborate Build and Reprogram with extra Delay wrappers since one is now required by the type - Division by zero, negative exponents, and bad comparisons now throw exceptions. Closes #150. Closes #226.
Describe the bug
build
command fails after passing through identity functionTo Reproduce
Expected behavior
x and (id x) should always be equivalent
The text was updated successfully, but these errors were encountered: