Actually, this is a bug, because the expression in a single-argument values call is forced prematurely.
eg, This should not error: -> (let-values ([(x) (values (error "a"))]) 1) ; a [,bt for context] Just like this does not error. -> (let-values ([(x y) (values (error "a") (error "b"))]) 1) 1 Lazy Racket is trying to preserve the (values x) == x from Racket, but since LR's force is recursive, this is actually impossible without breaking the semantics like it's doing now. Luke, thanks for finding this. If you want to submit a pull request, I will merge. (Just drop the first clause in the case-lambda entirely.) Maybe some extra tests would be nice as well :) Otherwise if you dont have time, let me know and I'll do it. > Beyond the library documentation, does anyone know if there are any > discussions or tutorials that go into the do's and don'ts of using #lang lazy > ? There isnt any. You can check out the Barzilay-Clements paper [1] to learn about the motivation behind LR, but otherwise LR should have "standard" lazy semantics. [1]: http://digitalcommons.calpoly.edu/cgi/viewcontent.cgi?article=1047&context=csse_fac On Thu, Jul 10, 2014 at 1:15 PM, Luke Whittlesey <[email protected]> wrote: > Thank you for the in-depth analysis. Very interesting. > > Following your reasoning, if I edit lazy.rkt and force `values` to use > `multiple-values` for the single entry case, the example that was previously > broken now works. (I just have no idea if this breaks something else in the > process.) > > at lazy.rkt line:223 > replace: > (define* ~values > (case-lambda [(x) x] [xs (multiple-values xs)])) > > with: > (define* ~values > (case-lambda [(x) (multiple-values (list x))] [xs (multiple-values > xs)])) > > > I had assumed that a reference to an identifier was delayed, so thanks for > showing that this is currently not the case. > > Beyond the library documentation, does anyone know if there are any > discussions or tutorials that go into the do's and don'ts of using #lang > lazy ? > > Thanks, > Luke > > > On Thu, Jul 10, 2014 at 6:24 AM, Matthew Flatt <[email protected]> wrote: >> >> I'm not sure whether to call it a bug or a limitation of `lazy`. >> >> The `lazy` language doesn't delay a reference to an identifier. As a >> result, >> >> (define x y) >> (define y (list 1)) >> (car x) >> >> fails. The case could be made that the right-hand side of the definition >> of `x` should have been a lazy reference to `y`, but that's not what >> `lazy` currently does. >> >> A problem with the current choice is that it interacts badly with `!`, >> especially as used by `letrec-values`. The implementation of >> `letrec-values` forces the right-hand side of a binding using `!` to >> determine how many values it produces. That works ok when the >> right-hand side is produced by `values` on more than one argument, >> because `values` produces a special multiple-values result that leaves >> its values unforced after `!`. When `values` get one argument, then it >> just returns the argument.... and that's still ok for something like >> `(values (list 1 (/ 0)))`, because the `(/ 0)` expression is lazy. >> >> In your example, the implicit use of `!` for the right-hand side of the >> A` binding produces `(! (list a B))`. That `B` is not itself treated as >> a lazy expression, so forcing the list to be constructed causes `B` to >> be evaluated early. >> >> You can make the variable reference lazy by wrapping it with `~`: >> >> (letrec-values ([(A) (values (list 'a (~ B)))] >> [(B) (values (list 'b A))]) >> B) >> >> Again, I don't know that you should have to do that, but it's how >> `lazy` is defined at the moment. >> >> At Mon, 7 Jul 2014 15:06:26 -0400, Luke Whittlesey wrote: >> > Hello all, >> > I've been playing around with creating circular lists (and learning >> > racket >> > which has been quite fun), but I'm stumped on why the lazy version of >> > letrec-values is not producing a promise like the lazy version of letrec >> > does. With the lazy letrec I can create circular lists, but with the >> > lazy >> > letrec-values I get #<undefined>. See the example below. >> > >> > ;;;;;;;;;;;;;;;;; example code ;;;;;;;;;;;;;;;;;;;;;;;;; >> > #lang lazy >> > >> > ;; create a circular list using letrec (this works) >> > (define example-working >> > (letrec ([A (list 'a B)] >> > [B (list 'b A)]) >> > B)) >> > (displayln "Working Example:") >> > (displayln example-working) >> > (displayln (!! example-working)) >> > >> > ; Prints... >> > ;Working Example: >> > ;(b #<promise:A>) >> > ;#0=(b (a #0#)) >> > >> > ;; create a circular list using letrec-values (this is broken) >> > (define example-broken >> > (letrec-values ([(A) (values (list 'a B))] >> > [(B) (values (list 'b A))]) >> > B)) >> > (displayln "Broken Example:") >> > (displayln example-broken) >> > (displayln (!! example-broken)) >> > >> > ; Prints >> > ;Broken Example: >> > ;(b (a #<undefined>)) >> > ;(b (a #<undefined>)) >> > ;;;;;;;;;;;;;;;;; end code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; >> > >> > I realize that there are many different ways to generate circular lists, >> > but why doesn't this work? Am I misunderstanding something or is this a >> > bug? >> > >> > Thanks, >> > Luke >> > ____________________ >> > Racket Users list: >> > http://lists.racket-lang.org/users > > > > ____________________ > Racket Users list: > http://lists.racket-lang.org/users > ____________________ Racket Users list: http://lists.racket-lang.org/users

