Consider:
(progn
(tons of code and bindings..)
(thousands of lets...
(read-an-arbitray-'expression-from-the-user)
(funcall #'eval that-expression)))
Now, if i want #'eval to be able to eval the expression with the
bindings currently in effect[1], do i have to go back and declare *all*
the thousands of variables ever used inside the code as special?
[1] in other words, as if i was using elisp, or i had dynamic
scope.. Or, in other words, i want the (funcall #'eval '<expression>)
to behave as if what was present there was the unquoted <expression>.
> Now, if i want #'eval to be able to eval the expression with the
> bindings currently in effect[1], do i have to go back and declare
> *all* the thousands of variables ever used inside the code as
> special?
There is no other way to generate dynamic bindings with let other than
to declare the symbols special, either globally or locally, no. If you
have thousands of bindings I assume they are autmatically generated,
and then it should be no problem to generate declarations as
well. Otherwise, a macro dynamic-let or some such might be
appropriate.
> [1] in other words, as if i was using elisp, or i had dynamic
> scope.. Or, in other words, i want the (funcall #'eval
> '<expression>) to behave as if what was present there was the
> unquoted <expression>.
That would imply that the lexical bindings were somehow explicitly
present at run-time, which I believe would defy much of the point of
lexical bindings (In other words, there are good reasons why it is not
so).
If you explain what it is you want to do, maybe we can suggest some
solutions.
--
Frode Vatvedt Fjeld
It's not related to your question, but why do you use (funcall #'eval
expression) instead of simply (eval expression)? FUNCALL is only necessary
when the function is not a literal.
--
Barry Margolin, bar...@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
> It's not related to your question, but why do you use (funcall #'eval
> expression) instead of simply (eval expression)? FUNCALL is only necessary
> when the function is not a literal.
indeed.. and perhaps i do not really use that extra unnecessary
funcall in my actual code.. just wasn't thinking enough when posting
the mesg :)
Another idea: why not take the user's expression, wrap the surrounding
material around that, and eval the entire thing as a capsule.
Backquote can be used to substitute material to set up the bindings,
and to insert the user expression itself:
(let (user-expression (outside-var 42))
;; ... get expression from user ...
(eval `(let ((inside-var ,outside-var))
,user-expression)))
Or what?
That should be:
(let (user-expression (outside-var 42))
;; ... get expression from user ...
(eval `(let ((inside-var ',outside-var))
,user-expression)))
The quote before ,outside-var would be important if the value of
outside-var were a list; without it, the list will be re-evaluated as an
expression.
hmm..
but then generating those declarations make them all
special, which in principle, can change the meaning of the some of the
rest of the code.. (right?)
i guess, as you mentioned, there is no way to "temporarily" make
everything special.
cool idea.. though i am afraid this won't really work for me.. to go
all the way up to the top such that all the (let)'s get wrapped would
mean traversal through many huge files.. and essentially, a complete
duplication of the entire system.. i wanted to 'add' that eval
functionality in one very-very tiny branch of the system..
i don't really need that 'eval... it was more of a conceptual
question..
(but i am surprised this cannot be done[1]....i used to use this sort
of a thing all the time in elisp..)
[1] (without completley redefining the entire code to be
dynamic-extent, that is)
It won't affect anything outside the LET.
In another post you said you've done this in Emacs Lisp. That's because
it *only* has dynamic bindings. If you copy the Elisp code and just add
all the declarations, you will most likely get the same semantics.
> i don't really need that 'eval... it was more of a conceptual
> question..
>
> (but i am surprised this cannot be done[1]....i used to use this sort
> of a thing all the time in elisp..)
Well, if you think about it a little more, it shouldn't be surprising.
You were able to do that in Elisp, precisely because elisp uses
dynamic binding. It would be possible to have a lexically-scoped Lisp
that could evaluate forms in a non-null environment, but you'd need to
be able to reify the current lexical environment, because unlike the
current dynamic environment, it's not normally available at runtime.
You could have a special form to reify the currenty lexical
environment, and a two-argument EVAL, like:
(let (...)
(flet (...)
(eval some-form (reify-lexical-environment))))
But this creates a whole mess of inefficiencies, and a real pain in
the ass for compiler-writers. In CL, the lexical environment only
exists as an object at compile-time (well, really
macroexpansion-time). To me that seems like one of the big advantages
of lexical scope, the compiler only needs to reify the environment
when it's compiling, and by runtime, all remnants of it are only
implicit. As soon as you put a call to REIFY-LEXICAL-ENVIRONMENT in
your code, the current environment, and all containing environments
would be set in stone. Everything would need to be available for
potential closing-over later. You couldn't ask questions about
reachability, because the answer would always be "I don't know". What
a mess.
> [1] (without completley redefining the entire code to be
> dynamic-extent, that is)
No, dynamic-extent is a very different thing than dynamic binding.
(let ((x (list 1 2 3)))
(declare (dynamic-extent x))
...)
Here, I am promising that the value for X will only be used in the
dynamic extent of the LET form. That is, no reference will be
retained after execution of the LET form has completed. Generally,
this gives the compiler license to stack-allocate the list (or
whatever you put in X).
--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'
Look up progv. You only need special declarations if you are going to do
bindings, and my guess is you are not going to want special bindings if
you allow bindings in the eval'ed expression.
--
In a fight against something, the fight has value, victory has none.
In a fight for something, the fight is a loss, victory merely relief.
70 percent of American adults do not understand the scientific process.
But what if you had a language feature to do this for you?
(defvar expression '(+ x x))
(special-eval-env (let ((x 2)) (special-eval expression))))
==> 4
The idea is that special-eval-env creates an environment within which
the (special-eval ...) form provides the eval behavior that you want. The
special-eval-env macro performs any necessary transformation on the code so
that you don't have to do it by hand.
This ``implementation'' below is just a crude hack to illustrate the concept.
It only handles let, not any other binding constructs. It does not recognize
quote or any special forms etc; it's a far, far cry from a code walker.
Also, it does not generate code to assign the values of the inner variables
back to the outer ones, so that.
(special-eval-env (let ((x 2)) (special-eval '(setf x 3) x))
yields 2, not 3.
The idea in this crude hack is to walk the code recursively. Wherever a
(special-eval ...) occurs, generate the form which binds the inner variables to
the values of the corresponding outer ones, based on keeping track of what
variables are bound.
(defun special-eval-expander (forms lexicals-list)
(let (new-lexicals)
(if (consp forms)
(case (first forms)
((let)
`(let ,(mapcar #'(lambda (var-form)
(cond
((consp var-form)
(push (first var-form) new-lexicals)
(special-eval-expander var-form lexicals-list))
(t (push var-form new-lexicals)
var-form)))
(second forms)) ,@(special-eval-expander
(rest (rest forms))
(append new-lexicals lexicals-list))))
((special-eval)
`(eval (list 'let (list ,@(mapcar #'(lambda (var) `(list ',var ,var))
lexicals-list))
,@(rest forms))))
(otherwise (cons (special-eval-expander (first forms) lexicals-list)
(special-eval-expander (rest forms) lexicals-list))))
forms)))
(defmacro special-eval-env (&body forms)
`(progn ,@(special-eval-expander forms nil)))
But what if you had a language feature to do this for you?
(defvar expression '(+ x x))
(special-eval-env (let ((x 2)) (special-eval expression))))
==> 4
The idea is that special-eval-env creates an environment within which
the (special-eval ...) form provides the eval behavior that you want. The
special-eval-env macro performs any necessary transformation on the code so
that you don't have to do it by hand.
This ``implementation'' below is just a crude hack to illustrate the concept.
It only handles let, not any other binding constructs. It does not recognize
quote or any special forms etc; it's a far, far cry from a code walker.
Also, it does not generate code to assign the values of the inner variables
back to the outer ones, so that.
(special-eval-env (let ((x 2)) (special-eval '(setf x 3)) x))
We have. Please look up progv.
thanks to all who replied here and privately.
PS: Several people wrote back explaining to me that elisp has dynamic
scoping and CL has lexical. Yeah, i understand that (atleast i think
i do.. some of it.. :-) even though i did confuse dynamic scoping ith
dynamic extent in a footnote). What i had wanted to do was to have
all my CL code, which was defined using lexical scope (and this can't
be changed) behave as if it had been declared using dynamic scope, as
far as the eval is concerned.. i guess that is too silly a demand..
No, it is not. The answer you seek is progv. I have replied to this
three times, now. I guess the reason you do not respond to it is that
you have to do some homework to value this answer. However, there is no
point in explaining anything more to you until you go do your howework
and learn what progv does. It takes time to explain, and the standard
does it very well. If you have questions, they should be based on your
understanding of its specification.
> I guess the reason you do not respond to it is that
> you have to do some homework to value this answer.
Or, possibly, your responses had not propagated to him yet.
Paul