From posting-system@google.com Sun Dec 30 18:41:48 2001
Date: Sun, 30 Dec 2001 15:41:44 -0800
From: oleg-at-pobox.com
Newsgroups: comp.lang.scheme
Subject: Re: Eval and Define
References: <3C2E26C3.519CB081@sonic.net>
X-Comment: a minor correction and an explanation about mutations on
Dec 31, 2001
Message-ID: <7eb8ac3e.0112301541.3bf596fd@posting.google.com>
Status: OR
Ray Dillinger wrote in message news:<3C2E26C3.519CB081@sonic.net>...
> I want to use something to add bindings to environments,
> then store those environments in variables so I can use
> them in later (eval) statements as environment specifiers.
It *is* possible to do what you want, in a portable way. The code will
not require first-class environments (which are present only in few
Scheme systems), and will work in _any_ R5RS system.
The basic idea was indicated in a message by All-in-one Petrofsky. You
have to evaluate _closed_ expressions. Suppose, for example, you wish
to evaluate a set of bindings
(define foo bar)
(define quux zoo)
and then evaluate
(+ foo quux)
What you need to do is to evaluate
(lambda (foo quux) (+ foo quux))
and then apply the result of that evaluation to (list bar zoo). If the
expression to evaluate is closed (i.e., it contains no free variables,
except those that are defined in r5rs-environment, etc), the
environment doesn't matter.
The following example automates the process. As you see, you merely
need to use my-define instead of define and my-eval instead of eval
(and remember to quote appropriately) [1]. Even "closures" are
possible. The example works in Gambit, Bigloo and SCM.
; My environment: the LIST of (NAME . VALUE) pairs
(define my-env '())
; Extend the my-env with the binding
(define (my-define name value)
(cond
((assq name my-env) =>
(lambda (binding) ; 'name' has already been defined: update its value
(set-cdr! binding value))) ; Indeed, repeated 'define' is equiv to set!
(else ; enter the new binding
(set! my-env
(cons (cons name value)
my-env)))))
; Evaluate a form within my environment
; Do remember to quote the 'form'
; We translate form into
; (lambda (names-from-my-env ...) form)
; evaluate it and then execute it, given the corresponding values
(define (my-eval form)
(let*
((names (map car my-env)) ; names of all the bindings
(values (map cdr my-env))
(lambdaized-form
(eval `(lambda ,names ,form)
(scheme-report-environment 5)))
)
(apply lambdaized-form values)))
; Example
(my-define 'add +)
(my-define 'foo 1)
(my-define 'incr ; make the closure
(my-eval '(lambda (x) (add x foo))))
(my-define 'foo 1000) ; the closure should keep the previous value of foo
(my-define 'bar 2001)
(for-each display
(list "Happy "
(my-eval '(incr bar))
" "
(my-eval 'foo)
" times!"
#\newline))
[1] It goes without saying that an expression submitted to my-eval should
not attempt to mutate bindings, i.e., use set! on variables defined
previously. However, mutation of values is OK. Therefore, one can emulate
(define foo 1)
(set! foo 2001)
(+ foo 1)
as
(my-define 'foo (list 1))
(my-eval '(set-car! foo 2001))
(display (my-eval '(+ 1 (car foo))))
That is, we should box all mutable data: we should do explicitly what
many Scheme systems do implicitly.