I've noticed that Gambit seems to require all internal definitions to be at the top of a body. It will give "Ill-placed 'define'" otherwise. Other Scheme implementations seem to allow interleaving definitions and expressions as long as the last form of a body is an expression.
The reason I care about this is that sometimes test code in Clojure involves wrapping an entire file in a top-level function which is called to run the tests. If there are assert statements between function definitions, I will cause a "Ill-placed 'define'" error. This isn't really common, but it is done in the ClojureScript tests for example.
In some cases I imagine I could tree-walk the forms and lift any nested definitions to the top-level to manually forward-declare them, and then replace the internal definitions with set!... but I'd like to avoid such non-local code transformations if possible.
Another option would be to unwind all lets and lambdas so each body only can contain one expression, such that
(let () 1 (define two 2) two) => (let () 1 (let () (two 2) two))
Not sure off-hand if this approach would have any downsides w.r.t. the compiled code?
Are there any other avenues I could explore to allow this interleaving to be expressed in Gambit?
Afficher les réponses par date
On Sat, Jul 06, 2013 at 07:07:16PM -0500, Nathan Sorenson wrote:
(let () 1 (define two 2) two) => (let () 1 (let () (two 2) two))
Evidently I've missed something when I thought I learned Scheme. What does let mean with an empty list of definitions? Just a way to start a new scope for define to work in? In that case, what's the (two 2) doing there? Is the empty list some kind of escape code?
-- hendrik
On 07/06/2013 08:07 PM, Nathan Sorenson wrote:
Another option would be to unwind all lets and lambdas so each body only can contain one expression, such that
(let () 1 (define two 2) two) => (let () 1 (let () (two 2) two))
Gambit follows R5RS, where internal defines are required to be at the beginning of a lambda body (which a let body really is) and where those defines are equivalent to a letrec. So the following prints #t:
(let () (define (myeven? x) (cond ((zero? x) #t) ((< x 0) (myodd? (+ x 1))) (else (myodd? (- x 1))))) (define (myodd? x) (cond ((zero? x) #f) ((< x 0) (myeven? (+ x 1))) (else (myeven? (- x 1))))) (display (myodd? 3)) (newline))
and is equivalent to
(let () (letrec ((myeven? (lambda (x) (cond ((zero? x) #t) ((< x 0) (myodd? (+ x 1))) (else (myodd? (- x 1)))))) (myodd? (lambda (x) (cond ((zero? x) #f) ((< x 0) (myeven? (+ x 1))) (else (myeven? (- x 1))))))) (display (myodd? 3)) (newline)))
Your transformation would mean that the binding of odd? would not be visible to the definition of even?: Loading
(let () (define (myeven? x) (cond ((zero? x) #t) ((< x 0) (myodd? (+ x 1))) (else (myodd? (- x 1))))) (let () (define (myodd? x) (cond ((zero? x) #f) ((< x 0) (myeven? (+ x 1))) (else (myeven? (- x 1))))) (let () (display (myodd? 3)) (let () (newline)))))
gives
frying-pan:~> gsi crap.scm *** ERROR IN myeven?, "crap.scm"@5.14 -- Unbound variable: myodd?
I believe that systems that allow interleaving expressions and definitions interpret a body using something called letrec* semantics, but since I'm not clear what that entails, someone else will need to explain it.
Brad