[racket] lazy letrec-values

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Thu Jul 10 06:24:54 EDT 2014

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

Posted on the users mailing list.