[plt-scheme] How is letrec different from letrec* in its use?

From: Jos Koot (jos.koot at telefonica.net)
Date: Sat Feb 14 06:48:52 EST 2009

According to the R6RS docs,

(letrec* ([x 1]
          [y 2])
  (+ x y))

is NOT equivalent to

(letrec ([x 1])
  (letrec ([y 2])
    (+ x y)))

It is equivalent with:
(let ((x #f) (y #f))
 (set! x 1)
 (set! y 2)
 (+ x y))

The difference between letrec and letrec* is that the letrec does not 
specify the order in which the values to be bound are evaluated and 
assigned, whereas letrec* does guarantee that the values are computed and 
assigned from left to right. Letrec* is not a nested letrec, whereas let* 
may be regarded as a nested let. Hence the correspondence between letrec and 
letrec* is quite different from that between let and let*. Notice that in 
PLT-scheme's letrec is like letrec*, but according to R6RS it might have 
been different as follows:

(letrec ((x 1) (y 2)) (+ x y)) -->

(let ((x undefined) (y undefined))
 (let ((tempx 1) (tempy 2)) ; in arbitrary order
  (set! x tempx) (set! y tempy) ; in arbitrary order
  (+ x y)))

and

(letrec* ((x 1) (y 2)) (+ x y)) -->

(let ((x undefined) (y undefined))
  (set! x 1) (set! y 2) ; from left to right
  (+ x y))

Jos

----- Original Message ----- 
From: "Grant Rettke" <grettke at acm.org>
To: "James Coglan" <jcoglan at googlemail.com>
Cc: "PLT-Scheme List" <plt-scheme at list.cs.brown.edu>
Sent: Saturday, February 14, 2009 6:01 AM
Subject: Re: [plt-scheme] How is letrec different from letrec* in its use?


> On Fri, Feb 13, 2009 at 6:35 AM, James Coglan <jcoglan at googlemail.com> 
> wrote:
>>> I just read the spec for both letrec and letrec* and I see that the
>>> former is not evaluted in any other but the latter is evaluted from
>>> left to right. I am not getting the difference in their use. May
>>> someone please provide an illuminating example?
>
>> (letrec) is like (let), but the evaluations take place *inside* the 
>> lambda,
>> changing their scope. The evaluations are not supposed to depend on each
>> other so evaluation order is not important (like for (let)).
>>
>> (letrec ([x 1]
>>          [y 2])
>>   (+ x y))
>>
>> is equivalent to:
>>
>> ((lambda ()
>>   (define x 1)
>>   (define y 2)
>>   (+ x y)))
>
> This is how I interpreted its behavior from the spec:
>
> (define-syntax my-letrec
>  (syntax-rules ()
>    ((_ ((var init) ...) body)
>     (let ((var #f) ...)
>       (set! var init) ...
>       body))))
>
> (my-letrec
> ((a (lambda (n) (when (< n 5) (b (add1 n)))))
>  (b (lambda (n) (when (< n 5) (a (add1 n))))))
> (a 0))
>
> #;(let ((a #f)
>        (b #f))
>    (set! a (lambda (n) (when (< n 5) (b (add1 n)))))
>    (set! b (lambda (n) (when (< n 5) (a (add1 n)))))
>    (a 0))
>
> Are we saying the same thing? Is this right?
>
>> Finally, (letrec*) is like (let*) but it uses (letrec).
>>
>> (letrec* ([x 1]
>>           [y 2])
>>   (+ x y))
>>
>> is equivalent to:
>>
>> (letrec ([x 1])
>>   (letrec ([y 2])
>>     (+ x y)))
>
> I see. This allow for mutually recursive definitions; but as Sam said
> you can predict about behavior in situations when you are not *only*
> binding lambda expressions.
>
> I am wondering why would people use letrec for anything other than
> binding lambda expressions if it is impossible to reason about its
> behavior? Or rather, why would it allow anything other than lambda
> expressions to be bound if you can't predict the behavior?
> _________________________________________________
>  For list-related administrative tasks:
>  http://list.cs.brown.edu/mailman/listinfo/plt-scheme 



Posted on the users mailing list.