>>8
Short answer: Different
letrec semantics. R5RS
letrec is ``broken'', R6RS and Racket fixed it.
Long answer: Internal definitions are equivalent to
letrec:
(lambda ()
(define x y) ...
body ...)
; =>
(lambda ()
(letrec ((x y) ...)
body ...))
R5RS'
letrec has unspecified evaluation order like
let, binding expressions can refer to the bindings, but cannot depend on them (evaluating them is an error).
So, this code:
(letrec ((x 1) (y (lambda () (+ x 1)))) (y))
evaluates to 2.
This code:
(letrec ((x 1) (y (+ x 1))) y)
is invalid R5RS. A strict implementation should reject this code the same way it rejects
(let ((x 1) (x 2)) x).
Racket's
letrec is really R6RS'
letrec*, which has sequential evaluation order like
let*. Binding expressions can depend to previous bindings, and
(letrec* ((x 1) (y (+ x 1))) y) returns 2 as you'd expect.
(letrec* ((x x)) x) and
(letrec* ((x y) (y 1)) x) still blow up, as they should.
letrec* exists to fix internal definition semantics to match top-level definitions, because people found
letrec semantics confusing.
Racket ditched R5RS'
letrec altogether and renamed
letrec* to
letrec, and here we are.