问题
I'm currently working on a LISP interpreter written in Java. Now I stuck at the closures. I want to enable closures like this:
(define a 1000)
(define closure (lambda (a) (lambda (b) (+ a b))))
(define x (closure 10))
(x 20) --> 30
So, (x 20)
should return 30
. But, guess what, it returns 1020 in my interpreter. I think the mistake is in my lambda class. It looks like this:
public class LLambda extends LOperation {
private LList parameters;
private LList definitions;
public LLambda(LList parameters, LList definitions) {
this.parameters = parameters;
this.definitions = definitions;
}
public LObject eval(Environment environment, LObject tokens) {
environment = environment.copy();
for(int i = 0; i < parameters.size(); i++) {
LSymbol key = LSymbol.create(parameters.get(i));
LObject object = ((LList) tokens).get(i);
object = object.eval(environment, tokens);
environment.put(key, object);
}
return definitions.eval(environment, tokens);
}
}
This class works fine, but it doesn't stores the environment values to enable closures. Has someone an idea how to do it? And where to do it? In the constructor or in the eval method?
And, if I don't execute this:
environment = environment.copy();
The closures works, but it breaks some other tests.
Thanks.
(I can also upload the whole source or give it free in GIT).
回答1:
This class works fine, but it doesn't stores the environment values to enable closures. Has someone an idea how to do it? And where to do it? In the constructor or in the eval method?
Yes, the class should store the environment. Generally speaking, a member variable. :)
It should be in the constructor because the environment is bound at the time the lambda is constructed, and not at eval time.
At eval time, the original environment is not available: the new environment is.
If your dialect is purely lexically scoped, then your lambda does not need an environment parameter. Remember, what is a lambda? It is a function. The evaluation of forms requires an environment. The evaluation of functions does not; the evaluation of a function is a function call, and that takes just arguments. Environments do not pass into functions; function bodies are evaluated in an encapsulated space with its own private environment. (The presence of an eval
function on a lambda even seems wrong; you want this to be named call
or something like that. The interpreted lambda uses the services of the evaluator; but it isn't one.)
Inside your lambda, the action will be to evaluate the forms of the lambda body. Those will use the stored environment (not anything that was passed in).
You have to establish an environment in which the lambda parameters have bindings to the argument values. This is nested within the captured environment. (I.e. an argument called x
shadows a captured variable called x
).
(You must already have some way of nesting environments; i.e constructing some new bindings which refer to an outer environment.)
If you want to support dynamic scoping in addition to lexical, there shouldn't be any need to pass an environment for that; it can be done implicitly. A thread-local variable can maintain the dynamic environment or something like that.
(The details depend on interpreter design; in some designs, there is always some context object passed around (representing the interpreter). If you go to, say, a byte code virtual machine with an explicit stack, you will need that.)
回答2:
I strongly suggest reading Christian Queinnec's book Lisp in Small Pieces which describes in great details many ways of implementing Lisp (or Scheme-like) evaluators, interpreters, compilers.
You'll need to handle differently closed values from local values.
来源:https://stackoverflow.com/questions/10161802/how-to-implement-closures-into-a-lisp-interpreter