views:

412

answers:

5

I have an s-expression bound to a variable in Common Lisp:

(defvar x '(+ a 2))

Now I want to create a function that when called, evaluates the expression in the scope in which it was defined. I've tried this:

(let ((a 4))
  (lambda () (eval x)))

and

(let ((a 4))
  (eval `(lambda () ,x)))

But both of these create a problem: EVAL will evaluate the code at the top level, so I can't capture variables contained in the expression. Note that I cannot put the LET form in the EVAL. Is there any solution?

EDIT: So if there is not solution to the EVAL problem, how else can it be done?

EDIT: There was a question about what exactly I am try to do. I am writing a compiler. I want to accept an s-expression with variables closed in the lexical environment where the expression is defined. It may indeed be better to write it as a macro.

+2  A: 

CLISP implements an extension to evaluate a form in the lexical environment. From the fact that it is an extension, I suspect you can't do that in a standard-compliant way.

(ext:eval-env x (ext:the-environment))

See http://clisp.cons.org/impnotes.html#eval-environ.

sanxiyn
A: 

In Common Lisp you can define *evalhook* Which allows you to pass an environment to (eval ...). *evalhook* is platform independent.

dsm
i can't find anything like *evalhook* in sbcl...
Attila Lendvai
very true, seems they got deleted by ANSI
dsm
+2  A: 

What is the actual problem that you want to solve? Most likely, you're trying to tackle it the wrong way. Lexical bindings are for things that appear lexically within their scope, not for random stuff you get from outside.

Maybe you want a dynamic closure? Such a thing doesn't exist in Common Lisp, although it does in some Lisp dialects (like Pico Lisp, as far as I understand).

Note that you can do the following, which is similar:

(defvar *a*)
(defvar *x* '(+ *a* 2))  ;'

(let ((a 10))
  ;; ...
  (let ((*a* a))
    (eval *x*)))

I advise you to think hard about whether you really want this, though.

Matthias Benkard
+4  A: 

You need to create code that has the necessary bindings. Wrap a LET around your code and bind every variable you want to make available in your code:

(defvar *x* '(+ a 2))

(let ((a 4))
  (eval `(let ((a ,a))
           ,*x*)))
Rainer Joswig
A: 

It is possible to use COMPILE to compile the expression into function and then use PROGV to FUNCALL the compiled function in the environment where variables are dynamically set. Or, better, use COMPILE to compile the expression into function that accepts variables.

Compile accepts the function definition as a list and turns it into function. In case of SBCL, this function is compiled into machine code and will execute efficiently.

First option (using compile and progv):

(defvar *fn* (compile nil '(lambda () (+ a 2)))
(progv '(a) '(4) (funcall *fn*))
=>
6

Second option:

(defvar *fn* (compile nil '(lambda (a) (+ a 2))))
(funcall *fn* 4)
=>
6
dmitry_vk