r{5,6}rs: change letrec to signal use-before-definition errors

The old `letrec` implementation used the R5RS macro with `undefined`,
which differents from Racket's `letrec` in that no variable is
initialized until all right-hand sides are evaluated.  It turns out
to be simpler and produce the same effect to use `letrec-values`
with a single clause: `(letrec ([x e1] [y e2]) ....)` ->
`(letrec-values ([(x y) (values e1 e2)]) ....)`.
This commit is contained in:
Matthew Flatt 2014-04-18 15:32:19 -06:00
parent 4216f3b666
commit 6112d337b3
3 changed files with 20 additions and 50 deletions

View File

@ -134,6 +134,13 @@ not allow redefinition of top-level bindings, and expressions
evaluated through @racket[load] and @racket[eval] cannot automatically
access bindings defined within the module.
@history[#:changed "6.0.1.4" @elem{When an identifier bound by
@racket[letrec] is referenced
before it is initialized, an
exception is raised, instead of
producing @|undefined-const|.}]
@; --------------------
@subsection{Non-@|r5rs| Bindings from @racketmodname[r5rs]}

View File

@ -242,55 +242,18 @@
;; None, so just use R5RS quote:
#'(r5rs:quote form))]))
;; Copied from R5rS, but with an added `let' around body,
;; and with optimization for precedure letrecs
(define-for-syntax (immediate-value? stx)
(let ([v (syntax-e stx)])
(or (number? v)
(boolean? v)
(string? v)
(syntax-case stx (r5rs:lambda quote r5rs:quote #%datum)
[(r5rs:lambda . _rest) #t]
[(quote . _) #t]
[(r5rs:quote . _) #t]
[(#%datum . _) #t]
[_ #f]))))
;; The difference between R5RS `letrec` and Racket `letrec` (or
;; R6RS `letrec*`) is that all right-hand sides are evaluated
;; before any binding is initialized. We can get this effect
;; by using `letrec-values` and a single clause:
(define-syntax (r5rs:letrec stx)
(syntax-case stx (r5rs:lambda)
((r5rs:letrec ((var1 rhs) ...) body ...)
(andmap immediate-value? (syntax->list #'(rhs ...)))
(syntax/loc stx (letrec ((var1 rhs) ...) (r5rs:body body ...))))
((r5rs:letrec ((var1 init1) ...) body ...)
(syntax/loc stx
(r5rs:letrec "generate_temp_names"
(var1 ...)
()
((var1 init1) ...)
body ...)))
((r5rs:letrec "generate_temp_names"
()
(temp1 ...)
((var1 init1) ...)
body ...)
(syntax/loc stx
(let ((var1 undefined) ...)
(let ((temp1 init1) ...)
(set! var1 temp1)
...
(let ()
(r5rs:body
body ...))))))
((r5rs:letrec "generate_temp_names"
(x y ...)
(temp ...)
((var1 init1) ...)
body ...)
(syntax/loc stx
(r5rs:letrec "generate_temp_names"
(y ...)
(newtemp temp ...)
((var1 init1) ...)
body ...)))))
(letrec-values ([(var1 ...)
(values init1 ...)])
(r5rs:body
body ...))))))
(define-syntax (r5rs:lambda stx)
;; Convert rest-arg list to mlist, and use r5rs:body:

View File

@ -382,10 +382,6 @@ several known ways:
@racket[string-upcase], and @racket[string-titlecase] are not
determined as specified by Unicode Standard Annex #29.}
@item{When an identifier bound by @racket[letrec] or @racket[letrec*]
is referenced before it is bound, an exception is not raised;
instead, the reference produces @|undefined-const|.}
@item{A custom textual port must represent positions using integers,
and the positions must correspond to bytes in a UTF-8 encoding
of the port's data. For custom ports (byte or character) that
@ -407,9 +403,13 @@ several known ways:
@racketidfont{#%top}, and @racketidfont{#%top-interaction} are
imported into every library and program, and at every phase
level for which the library or program has imports.}
]
@history[#:changed "6.0.1.4" @elem{When an identifier bound by
@racket[letrec] or @racket[letrec*]
is referenced before it is initialized,
an exception is raised, instead of
producing @|undefined-const|.}]
@; ----------------------------------------------------------------------