eval using current function's namespace
The following racket function produces the erro开发者_如何学Cr:
reference to undefined identifier: val
This is because the eval function looks at the global namespace, not the local function's namespace. How do I trick eval into using the local function's namespace?
(define some-eval!
(lambda (val row col)
(eval (list 'define 'ttboard '(list-builder val row col))) (current-namespace) ))
Your question is specific to Racket. In general different Scheme implementations have various approaches to this problem, but in almost all cases you will not get eval
to handle local bindings like you're trying to do. But specific to the Racket case, you should read the evaluation section in the Racket guide -- it explains why things aren't working like you want them to, and it shows how to get the module scope to work.
Just as a quick summary -- the reason local bindings are not visible to eval
is that this means that (lambda (x) x)
and (lambda (y) y)
cannot be compiled to the same function, since the names can make a difference. You could argue that the compilation could depend on whether eval
is used inside the function or not -- but that's something that cannot be determined at compile-time. For example:
(define (foo f) (let ([x 1]) (f 'x)))
(foo eval)
In this case, there is no way for the compiler to tell that foo
is ever going to be called with eval
.
Finally, there are similar difficulties in other languages that try to tackle eval
. For example, in JS, eval
is a magical thing that can affect the way a function is compiled, so something like:
function foo(x) { alert("x = " + eval("x")); }
will actually work -- but it requires eval
to be physically present in the body of the function. If that's not done, then things can break. For example, this code:
function foo(x,f) { alert("x = " + f("x")); }
foo(123,eval);
works for me in Fx, but fails in Chrome, saying that x is not defined
. And if that's not enough to demonstrate the mess, consider this:
function foo(x,f) { alert("x = " + f("x")); }
var x = 456;
foo(123,eval);
shows 456 in Chrome and in Fx.
You can define ttboard
ahead of time and then set!
it:
(define ttboard #f)
(define create-board
(lambda (val row col)
(set! ttboard (list-builder val row col))))
That way, you can clearly tell that ttboard
is a global variable, rather than having its definition obscured in an eval'd clause.
I think that, for what you want to do, it may be enough to set the values of val
, row
, and col
at expression-composition time, rather than have the interpreter grab the values of val
et al. at eval-time (impossible in Racket).
(define some-eval!
(lambda (val row col)
(eval (list 'define 'ttboard `(list-builder ,val ,row ,col)))
(current-namespace)))
Note that there is almost certainly a nicer, cleaner way to accomplish what you are trying to do without eval
, unless, of course, you are doing this to practice using eval
.
In general, there's no way to access the lexical environment with eval
, as Eli points out. However, I believe that this workaround will do what you want:
#lang racket/load
(define some-eval!
(lambda (val row col)
(namespace-set-variable-value! 'val val)
(namespace-set-variable-value! 'row row)
(namespace-set-variable-value! 'col col)
(eval (list 'define 'ttboard '(list-builder val row col))
(current-namespace))))
(define list-builder list)
(some-eval! 1 2 3)
(display ttboard)
精彩评论