1

For every 100% compliant R7RS-small program that does not rely on any implementation-specific or undefined behavior, is it true that every instance of letrec in the program can be replaced with letrec* without causing any change in behavior? In other words, is there any R7RS-small program where an appearance of letrec cannot be substituted with letrec*?

Flux
  • 9,805
  • 5
  • 46
  • 92

1 Answers1

2

I think that the answer is yes, it can, assuming that the form is not 'an error' in R7RS terminology (but see note at end). In particular I think that if there's a form like

(letrec ((v1 <e1>) (v2 <e2>)) ...)

Then it must be possible to evaluate <e2> without referring to the value of v1, but that binding does actually exist when <e2> is evaluated: it is just an error to refer to it. So in particular this is not allowed:

(let ((a 1)) (letrec ((a 2) (b a)) ...))

because the binding that the init for b refers to is that established by the letrec, not that established by the let, but it is not yet legal to refer to the value of that binding.

That being the case then if you simply replace letrec by letrec* then <e2> still will not refer to the value of v1 and thus the results will be the same.

The converse is not true:

(letrec* ((a 1) (b a)) ...)

is fine, but you can't replace the letrec* by letrec there.

That being the case I'm unclear what useful purpose letrec serves (perhaps this is why Racket's letrec has the semantics of Scheme's letrec*).


Note an earlier version of this answer came to the opposite conclusion. I am now not convinced I understand things well enough.

ignis volens
  • 7,040
  • 2
  • 12
  • I tried the `letrec` example in MIT Scheme 11.2, Chibi 0.10.0, and Guile 3.0.7. MIT Scheme gives an error: `Unassigned variable: a`, Chibi returns `2`, and `guile --r7rs` returns `2`. Why do they not return `1` as you claim? – Flux Dec 01 '21 at 15:15
  • Yes, I am not sure my answer is right – ignis volens Dec 01 '21 at 15:22
  • @Flux: I think I was wrong: the new version of the answer comes to the opposite conclusion: yes, you can do this. However I think we need a proper Scheme expert not an old lisp hacker who thinks they understand Scheme but perhaps does not like me. – ignis volens Dec 01 '21 at 16:06
  • "if you simply replace `letrec` by `letrec*` then `` still will not refer to the value of `v1` and thus the results will be the same." — Are you sure about this? `(letrec* ((a 2) (b a)) a)` returns `2`. – Flux Mar 22 '22 at 02:15
  • @Flux: Yes, that's right. `(letrec ((a 2) (b a)) ...)` is an error since the form `a` refers to the value of the binding of `a`: what I claim is that all `letrec` forms *which are not errors* can be replaced by `letrec*` forms. If *all* `letrec` forms are blindly replaced by `letrec*` forms then some errors will become legal code, like this case. – ignis volens Mar 22 '22 at 09:51