From 6112d337b332aabdfb5d12cd12443f48c20fa06e Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Fri, 18 Apr 2014 15:32:19 -0600 Subject: [PATCH] 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)]) ....)`. --- pkgs/r5rs-pkgs/r5rs-doc/r5rs/r5rs.scrbl | 7 +++ pkgs/r5rs-pkgs/r5rs-lib/r5rs/main.rkt | 53 +++---------------- .../r6rs-doc/r6rs/scribblings/r6rs.scrbl | 10 ++-- 3 files changed, 20 insertions(+), 50 deletions(-) diff --git a/pkgs/r5rs-pkgs/r5rs-doc/r5rs/r5rs.scrbl b/pkgs/r5rs-pkgs/r5rs-doc/r5rs/r5rs.scrbl index 54e7ed9f9f..b908910596 100644 --- a/pkgs/r5rs-pkgs/r5rs-doc/r5rs/r5rs.scrbl +++ b/pkgs/r5rs-pkgs/r5rs-doc/r5rs/r5rs.scrbl @@ -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]} diff --git a/pkgs/r5rs-pkgs/r5rs-lib/r5rs/main.rkt b/pkgs/r5rs-pkgs/r5rs-lib/r5rs/main.rkt index 4061636345..59f3c9524a 100644 --- a/pkgs/r5rs-pkgs/r5rs-lib/r5rs/main.rkt +++ b/pkgs/r5rs-pkgs/r5rs-lib/r5rs/main.rkt @@ -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: diff --git a/pkgs/r6rs-pkgs/r6rs-doc/r6rs/scribblings/r6rs.scrbl b/pkgs/r6rs-pkgs/r6rs-doc/r6rs/scribblings/r6rs.scrbl index e73185dc85..c8463470a2 100644 --- a/pkgs/r6rs-pkgs/r6rs-doc/r6rs/scribblings/r6rs.scrbl +++ b/pkgs/r6rs-pkgs/r6rs-doc/r6rs/scribblings/r6rs.scrbl @@ -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|.}] @; ----------------------------------------------------------------------