Scheme complains that in the code below, the x
in (+ x 1)
is undefined:
(letrec ((x 1)
(y (+ x 1)))
y)
- Chez Scheme:
Exception: attempt to reference undefined variable x
- MIT Scheme:
;Unassigned variable: x
So, I defined another x
:
(let ((x 999))
(letrec ((x 1)
(y (+ x 1)))
y))
However, the Scheme implementations still complain that the x
in (+ x 1)
is undefined. What is going on? Clearly, I have already defined x
.
That's not how
letrec
works. You can think ofletrec
as if it did something close to this (see R7RS, 4.2.2 for instance).is equivalent to
Where
<illegal-value>
is some thing you are never meant to refer to, and which implementations may make illegal to reference.This is not quite what
letrec
does, because in the above expansion the assignments happen in a defined order, whereas inletrec
the evaluations of the initialisation forms do not. So a fancier expansion might be usinglet
first, since its order of evaluations is also non-defined:Now we can see that
will fail, because the expansion inserts another
let
which shadows the outer binding:and you can see that what is going wrong is when
t2
is bound toa
which refers to the current value of the binding ofa
which is<illegal>
, while the outera
whose value is999
is inaccessible.On the other hand, this:
both will work, and will return true, because in the expansion
(b)
will be called after theset!
s.The most common use of
letrec
is that it allows recursive functions to be bound: in something likeThen
f
needs to refer to the same binding off
in order to work, but it does not actually look at the value of that binding until the function is called, by which time all is well.See my other answer for some other possible uses of
letrec
however.