4

Can anybody explain me how eval works with emacs24? From eval description:

eval is a built-in function in `C source code'.

(eval FORM &optional LEXICAL)

Evaluate FORM and return its value.
If LEXICAL is t, evaluate using lexical scoping.

Does that mean, that something like this should work?

(setq lexical-binding t)
(let ((myvarr 42)) (eval 'myvarr t)) ; (void-variable myvarr)

Update:

(setq lexical-binding nil)
;; => nil
(let ((myvarr 42)) (eval 'myvarr))
;; => 42 (#o52, #x2a, ?*)
(setq lexical-binding t)
;; => t
(let ((myvarr 42)) (eval 'myvarr))
;; Debugger entered--Lisp error: (void-variable myvarr)
;;   eval(myvarr)
;;   (let ((myvarr 42)) (eval (quote myvarr)))
;;   (progn (let ((myvarr 42)) (eval (quote myvarr))))
;;   eval((progn (let ((myvarr 42)) (eval (quote myvarr)))) t)
;;   eval-last-sexp-1((4))
;;   eval-last-sexp((4))
;;   call-interactively(eval-last-sexp nil nil)
;;   call-last-kbd-macro(nil kmacro-loop-setup-function)
;;   kmacro-call-macro(nil nil)
;;   kmacro-end-or-call-macro(nil)
;;   call-interactively(kmacro-end-or-call-macro nil nil)
(ignore-errors (let ((myvarr 42)) (eval 'myvarr)))
;; => nil
(setq lexical-binding nil)
;; => nil
(eval (let ((myvarr 42)) (eval 'myvarr)) t)
;; => 42
(eval '(let ((myvarr 42)) (eval 'myvarr)) t)
;; Debugger entered--Lisp error: (void-variable myvarr)
;;   eval(myvarr)
;;   (let ((myvarr 42)) (eval (quote myvarr)))
;;   eval((let ((myvarr 42)) (eval (quote myvarr))) t)
;;   eval((eval (quote (let ((myvarr 42)) (eval (quote myvarr)))) t) nil)
;;   eval-last-sexp-1((4))
;;   eval-last-sexp((4))
;;   call-interactively(eval-last-sexp nil nil)

Emacs version: GNU Emacs 24.1.1 (i386-mingw-nt6.1.7600) of 2012-06-10 on MARVIN

desudesudesu
  • 2,185
  • 1
  • 15
  • 20

3 Answers3

7

Since lexical binding breaks much existing elisp code, it is an opt-in feature.

lexical scoping can best be understood with a simple example:

(defun some-func (callback)
 (let ((a 5))
  (funcall callback)))

(let ((a 3))
 (some-func (lambda () a)))

Under most languages, this would return 3 since the a in some-func doesn't seem visible from the bottom form. However, in emacs before 24 or without lexical scope this program returns 5.

This has led to many unexpected surprises and subtle and often hidden bugs between interacting functions; to fix this emacs 24 introduced lexical scoping, but as mentioned previously, is backwards compatible.

The mechanism to opt-in to dynamic scoping is either the file variable lexical-binding (which turns it on per source file) or as the option you see to eval

So if we rewrite the example to use eval:

(eval '(let ((a 3))
        (some-func (lambda () a))) nil) ; => 5

(eval '(let ((a 3))
        (some-func (lambda () a))) t) ; => 3

In your example, what is making the difference is not whether the code inside eval is dynamically scoped, but whether the code surrounding it is. Variable binding is what is affected by lexical scoping, not variable lookup. I'm not completely certain of eval's semantics here, but what seems to be happening (and what makes the most sense) is eval evaluates the expression in an entirely new lexical context. So the outer lexical scope is hidden from the inside of eval, but the dynamic scope is still visible (so the lookup succeeds when the file is dynamically scoped, but not otherwise).

cobbal
  • 69,903
  • 20
  • 143
  • 156
  • Oh, got it now. So, it's same as ``lexical-binding`` variable. Then I misunderstood it. So, as in CL, there is no way to eval lexical variables(return 42 in my OP-post example)? – desudesudesu Jun 15 '12 at 19:12
  • @desudesudesu your example should return 42 whether or not `LEXICAL` is set. I'm a little unsure of what you're asking. – cobbal Jun 15 '12 at 19:18
  • I updated OP-post. Could that behavior be explained? UPD: Yep, my fault. It's totally clear, and it do NOT return 42. – desudesudesu Jun 15 '12 at 19:40
  • @desudesudesu Ah, I see where my mistake was now; updated post attempting to explain. (it gets somewhat badly written and hand-wavy though, so let me know if any of it is unclear) – cobbal Jun 15 '12 at 19:51
  • Found it very useful to use this motto: "use ``(eval '(let ... ) t)`` when creating closure". This way you can use both the power of dynamic and lexical scopes. – desudesudesu Jun 15 '12 at 22:32
  • 1
    @desudesudesu: As a general rule, if you need `eval` you're probably doing something wrong. – Stefan Jun 19 '12 at 02:25
  • @Stefan: As a general rule, I hate this phrase. Don't understand me wrong, I know that eval CAN be used in a bad way(but that's not the reason I must hear the same-same phrase every time someone starts to talk about eval). Eval used in our case DOES NOT create harmful code in any way, because the thing under quotation meant to be evaluated in any way, and it's the only way to switch to lexical-scope that I can see now, coz even (progn (setq lexical-binding t) (prog1 (let ((counter 0)) (lambda () counter)) (setq lexical-binding nil))) does not create closures. – desudesudesu Jun 20 '12 at 23:11
  • You don't need eval to combine dynamic scoping and lexical scoping: setting `lexical-binding` to t in your file-local variables is all that's needed. Then use `defvar` to declare the variables which need to use dynamic scoping. The problem is not that `eval` can be used in harmful ways, it's that `eval` itself has many downsides, so you should only use it when it's *really* needed. – Stefan Jun 27 '12 at 16:22
  • @Stefan That way all function's args will be lexical, and it's not like you'll always want that. And I really don't like it to be global variables, I like the way it was in elisp, and i hate CL's variant of it. Which are ``eval``'s downsides? Can it be like more info here? – desudesudesu Jul 04 '12 at 07:31
6

I could not find the formal semantics of the eval function in Emacs 24, but all of your examples make sense when assuming that it works in the same way as that in Common Lisp (as indicated by cobbal). The Common Lisp HyperSpec says:

Syntax:

eval form

Description:

Evaluates form in the current dynamic environment and the null lexical environment.

Let's look at your examples one by one with the description in mind.

(setq lexical-binding t)
(let ((myvarr 42)) (eval 'myvarr t)) ; Lisp error: (void-variable myvarr)

Lexical binding is enabled by setting t to lexical-binding, so myvarr turns to be a lexically bound variable, which is not available inside the eval function as stated above. The eval function's option, t, is irrelevant here.

(setq lexical-binding nil)
(let ((myvarr 42)) (eval 'myvarr)) ; 42

Lexical binding is disabled by setting nil to lexical-binding, so myvarr turns to be a dynamically bound variable, which is available inside the eval function. The eval function's option, implicilty nil, is irrelevant here.

(setq lexical-binding t)
(let ((myvarr 42)) (eval 'myvarr)) ; Lisp error: (void-variable myvarr)

Lexical binding is enabled by setting t to lexical-binding, so myvarr turns to be a lexically bound variable, which is not available inside the eval function. The eval function's option, implicilty nil, is irrelevant here.

(ignore-errors (let ((myvarr 42)) (eval 'myvarr))) ; nil

Ditto.

(setq lexical-binding nil)
(eval (let ((myvarr 42)) (eval 'myvarr)) t) ; 42

Lexical-binding is disabled by setting nil to lexical-binding, so myvar turns to be a dynamically bound variable, which is available inside the inner eval function. Note that the let form, including the inner eval function, is evaluated as argument preparation before the outer eval is called. Neither the outer nor inner eval function's option is relevenat here.

(eval '(let ((myvarr 42)) (eval 'myvarr)) t) ; Lisp error: (void-variable myvarr)

myvarr turns to be a lexically bound variable, which is not available inside the inner eval. Note that, because of ', the let form is evaluated by the outer eval function with lexical binding enabled. The outer eval function's option is relevant here while the inner eval function's is not.


Why the null lexical environment?

That is, I think, because that alpha-equivalence would not hold anymore if the current lexical environment were used.

Alpha-equivalence is a formal way to say that the names of function parameters are not important. For example, (lambda (x) x) and (lambda (y) y) are alpha-equivalent, and we have regarded them as the same. Alpha-equivalence allows us to change a function parameter's name as we wish at any time. We take it for granted, and we will be very surprised if it does not hold. See Lambda calculus and Alpha-equivalence for more formal explanation.

But alpha-equivalence turns out to have some problem when a code value involving a free variable (called open code) can be passed around as in Lisp. Let's see the following example:

;;; -*- lexical-binding: t -*-
(lambda (x) (lambda (y) (eval x))) '(1+ y)

If eval evaluated under the current lexical environment, the form would be equivalent to (lambda (y) (1+ y)). Now see the following program:

(lambda (x) (lambda (z) (eval x))) '(1+ y)

This program is different from the previous one only in its parameter's name, z in place of y, so we naturally expect them to behave in the same way. But the latter program evaluates to (lambda (z) (1+ y)), which is definitely different from (lambda (y) (1+ y). Think about what will happen when the resulting function values are applied to the same argument. The point is that the name of a function parameter DOES matter when a code value containing a free variable is allowed.

Here we have two choices in order to preserve alpha-equivalence: we cannot give up alpha-equivalence because it feels so natural and we have been so much accustomed to it. The first option is to have eval evaluated under the null lexical environment as (Common) Lisp does. With this option, the free variable y in (1+ y) does not bind to the formal parameter y in (lambda (y) ...). So those two program behave consistently and alpha-equivalence is preserved well. The other choice is to preclude the problem by not allowing open code value from the beginning. I have heard that it is a method chosen by MetaOCaml

Someone may ask "if so, why the current dynamic environment?". For dynamically bound (a.k.a. special) variables, we already acknowledge well that their names are so important and that, if you change them carelessly, your program will be broken.

dkim
  • 3,930
  • 1
  • 33
  • 37
  • Yeah, I already understood everything about it. There was "update" for first post, where I understood that I forgot to quote for eval, and with the next answer I was explained about (eval arg t) feature. Still, I don't get, why they did ``eval`` to act that way(see my comment to Jisang Yoo). – desudesudesu Aug 20 '12 at 17:02
  • How do you think `eval` should behave? – dkim Aug 20 '12 at 17:13
  • Why not to find variables inside lexical environment? Any optional flag for that behavior? It's not built at compile-time, and I can actually get those variables with macro. So, why? – desudesudesu Aug 21 '12 at 16:09
  • @desudesudesu I have added an explanation about "Why the null lexical environment?". I hope it can answer your question. – dkim Aug 21 '12 at 18:35
  • Well yea. But I don't really see why this is so important, that there is no option to make it optional or at least make another function. – desudesudesu Aug 21 '12 at 19:56
  • 1
    I'm afraid that I might not understand your doubt well. Can we just use a dynamic variable if we want a variable to behave dynamically? – dkim Aug 21 '12 at 21:00
2

The odd behavior of your inner eval forms seems similar to that of symbol-value or add-to-list under lexical binding.

emacs lexical scoping and quoted variables

  • Under lexical binding, a symbol's value cell only holds global values, (which can be different from the variables' lexical binding value).

  • Use of quoted variables (such as in (eval 'var), (eval '(print var)), (add-to-list 'var 1)) only get or set on the symbol's value cell.

Practices to avoid encountering this kind of gotcha are:

  • Try to define and use macros rather than eval whenever possible

  • If you want to create/declare a global variable, refrain from using setq to create it, use defvar, defcustom or defconst to create it as a special variable instead. Special variables are always dynamically scoped, even in lexical binding mode.

Community
  • 1
  • 1
Jisang Yoo
  • 3,670
  • 20
  • 31
  • I just don't understand: WHY. I can write macro that will do all the things right. Why try to act so close to CL? ``(defmacro symbol-value-in-lexical-closure(arg) `(cdr (assoc ,arg (cadr (lambda()))))) (defmacro super-eval(arg) `(or (symbol-value-in-lexical-closure ,arg) (eval ,arg)))`` – desudesudesu Aug 19 '12 at 20:57