[racket] lazy letrec-values

From: Luke Whittlesey (luke.whittlesey at gmail.com)
Date: Fri Jul 11 16:00:30 EDT 2014

I just sent a pull request : https://github.com/plt/racket/pull/727

I added a few simple test cases, but the interesting thing is that running
`./racket/bin/raco test pkgs/lazy` seems to pass even when I add a failing
test case such as  `(! (eq? 1 2)) => #t`.
That same test will fail as expected if I run it within drracket.

Thank you all for your time and help. (And thank you for creating such a
nice community and toolset!)

-Luke






On Fri, Jul 11, 2014 at 12:02 AM, Spencer Florence <
florence at northwestern.edu> wrote:

> It looks like he has taken those down. But I'm sure he would email a copy
> if asked (the think is http://pl.barzilay.org/resources.html#classnotes
> btw)
>
>
> On Thu, Jul 10, 2014 at 8:42 PM, Matthias Felleisen <matthias at ccs.neu.edu>
> wrote:
>
>>
>> There are also Eli's class notes. I don't have a URL handy but I am sure
>> if you google "Eli Barzilay" and "course" you'll find his notes on the
>> various levels of lazy (plus homework assignments :-) -- Matthias
>>
>>
>>
>>
>>
>>
>> On Jul 10, 2014, at 6:41 PM, Stephen Chang wrote:
>>
>> > 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
>> > <luke.whittlesey at gmail.com> 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 <mflatt at cs.utah.edu>
>> 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
>>
>>
>> ____________________
>>   Racket Users list:
>>   http://lists.racket-lang.org/users
>>
>
>
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20140711/f0a51708/attachment.html>

Posted on the users mailing list.