0

If I have implemented letrec* in my Scheme interpreter, can I simply implement letrec by making it the same as letrec*? Is this allowed by the Scheme standards?

Flux
  • 9,805
  • 5
  • 46
  • 92
  • No, there's a lot of cases where you don't want this scoping behaviour. – SK-logic Nov 25 '21 at 15:58
  • In which language do you implement the interpreter ? – alinsoar Nov 29 '21 at 01:35
  • @alinsoar The interpreter will be implemented in either Scheme or OCaml. Does it matter? – Flux Nov 29 '21 at 07:34
  • @Flux Of course, it is a huge difference. – alinsoar Nov 29 '21 at 22:34
  • @SK-logic Could you provide a small example where a `letrec` cannot be replaced with a `letrec*`? – Flux Dec 01 '21 at 12:30
  • @Flux - sure, (define (Fa ...) ...) (letrec* ((Fa ...) (Fb (something something (Fa)))) ...) will have a very different semantics from an equivalent letrec - it'll mask the definition of Fa used in Fb, and this may not be something you wanted, especially if this was a result of some macro expansion. Therefore, to retain a control over scoping, you must have both letrec and letrec*. – SK-logic Dec 01 '21 at 18:34
  • @SK-logic Your example shows that it is not possible to replace every `letrec*` with a `letrec`. But [can every letrec be replaced with letrec*?](https://stackoverflow.com/questions/70184399/can-every-letrec-be-replaced-with-letrec) – Flux Dec 02 '21 at 01:59
  • @Flux, this example shows that `letrec*` won't be able to replace `letrec`, because it'll mask the definition of Fa used in Fb. – SK-logic Dec 02 '21 at 20:12
  • @SK-logic In the example, the first `Fa` is a procedure. What is the second `Fa`? Is it a procedure too? What is the first and second `something`? Could you provide a concrete example that can be run in a Scheme implementation? I am really trying to understand this. Thank you for helping. – Flux Dec 03 '21 at 10:21
  • @SK-logic I don't understand what you mean about "masking". If we have `(define (fa x) (+ x 1)) (letrec ((fa (lambda (x) (+ x 99))) (fb (lambda () (fa 1)))) (fb))`, the result is always `100`; `fa` is "masked" when using either of `letrec` or `letrec*`. Is there any code where `letrec` cannot be replaced with `letrec*`? – Flux Dec 03 '21 at 10:28
  • Ok, my example was wrong, Fa should have been evaluated in Fb intiialiser - then there'll be the difference. – SK-logic Dec 04 '21 at 11:38

1 Answers1

2

Hmmm...

r6rs uses the language "It should be possible to evaluate each <init> without assigning or referring to the value of any <variable>" (emphasis added), but I think the formal semantics indicates that implementations must raise an exception:

$ scheme
> (letrec ([x 1]
           [y (+ x 1)])
    y)
Exception: attempt to reference undefined variable x

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

( (alias letrec letrec*) is Chez to make letrec the same as letrec* )

r7rs provides a letrec macro expanding to let and set! (page 69 of the PDF):

(define-syntax letrec
        (syntax-rules ()
  ((letrec ((var1 init1) ...) body ...)
    (letrec "generate temp names"
             (var1 ...)
             ()
             ((var1 init1) ...)
             body ...))
  ((letrec "generate temp names" ()
             (temp1 ...)
             ((var1 init1) ...)
             body ...)
           (let ((var1 0 #|<undefined>|#) ...)
             (let ((temp1 init1) ...)
               (set! var1 temp1)
               ...
               body ...)))
  ((letrec "generate temp names" (x y ...)
             (temp ...)
             ((var1 init1) ...)
             body ...)
  (letrec "generate temp names" (y ...)
             (newtemp temp ...)
             ((var1 init1) ...)
             body ...))))

(nb <undefined> replaced by 0) Using this produces:

> (letrec ([x 1]
           [y (+ x 1)])
    `(,x . ,y))
(1 . 1)
>

(One can (expand '(letrec ([x 1] [y (+ x 1)]) y)) in Chez Scheme to see why)

(edit: Ghuloum & Dybvig 2009 Fixing Letrec (reloaded) (PDF) discusses letrec and letrec*)

mnemenaut
  • 684
  • 2
  • 11
  • The following paragraph to the 'should' one begins 'Implementation responsibilities: Implementations must detect references to a during the evaluation of the expressions (using one particular evaluation order and order of evaluating the expressions). If an implementation detects such a violation of the restriction, it must raise an exception \[...]'. I think that makes it reasonably clear that the 'should' should be a 'must', even without reading the formal semantics. – ignis volens Nov 26 '21 at 13:39