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.