Behaviour of EVAL inside LET

Discussion of Common Lisp
abvgdeika
Posts: 20
Joined: Mon Jun 06, 2011 10:59 pm

Behaviour of EVAL inside LET

Post by abvgdeika » Fri Oct 19, 2012 2:36 am

I found the following example of a strange behaviour of command EVAL inside LET constructions. Executing the code

Code: Select all

(setf z 666)
(let ((z 14))
    (print z)
    (eval '(setf www z)) 
  )
results in printing the value 14 of the variable z inside LET construction, but the variable www takes the value 666, not 14!

A very similar code

Code: Select all

(setf z 666)
(let ((z 14))
    (print z)
    (setf www z)
  )
results in the value 14 of the variable www, as expected.
Can someone explain this situation?
To understand LISP, you must first understand LISP.

Goheeca
Posts: 271
Joined: Thu May 10, 2012 12:54 pm
Contact:

Re: Behaviour of EVAL inside LET

Post by Goheeca » Fri Oct 19, 2012 4:48 am

Because eval evaluates its argument in the null lexical environment.
The environment can be captured to an environment object by a macro then to be passed to another macro but the environment object is implementation-dependent so you can't easily build lexical environment from that.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version). Temporary mirrors of aferomentioned: CLHS and a dark version.

abvgdeika
Posts: 20
Joined: Mon Jun 06, 2011 10:59 pm

Re: Behaviour of EVAL inside LET

Post by abvgdeika » Fri Oct 19, 2012 5:54 am

Thank you for the explanation!
U found the right code which does what expected and still uses EVAL:

Code: Select all

(setf z 666)
(let ((z 14))
    (eval `(setf www ',z))
  )
It is a bit involved combination of ` ' and , characters before variable z but the result is the value 14 of variable www, as expected!
To understand LISP, you must first understand LISP.

Paul
Posts: 106
Joined: Tue Jun 02, 2009 6:00 am

Re: Behaviour of EVAL inside LET

Post by Paul » Fri Oct 19, 2012 6:33 am

abvgdeika wrote:I found the following example of a strange behaviour of command EVAL inside LET constructions. Executing the code

Code: Select all

(setf z 666)
(let ((z 14))
    (print z)
    (eval '(setf www z)) 
  )
results in printing the value 14 of the variable z inside LET construction, but the variable www takes the value 666, not 14!

A very similar code

Code: Select all

(setf z 666)
(let ((z 14))
    (print z)
    (setf www z)
  )
results in the value 14 of the variable www, as expected.
Can someone explain this situation?
Try this in your lisp:

Code: Select all

(defun try (x)
(print z))

(setf z 666)
(let ((z 14))
 (try))
Do you know why it prints 666 instead of 14? The reason is the same as why your code acts the way it does (but note that it doesn't have to...what you've written is not strictly legal in Common Lisp; in my implementation it sets www to 14, and (try) prints 14. Why? Because (setf z 666) doesn't have any meaning when z doesn't exist -- you need a DEFVAR or DEFPARAMETER, or a LET or something, to make a variable called z before you can say (setf z 666), and you haven't got one.)

Konfusius
Posts: 62
Joined: Fri Jun 10, 2011 6:38 am

Re: Behaviour of EVAL inside LET

Post by Konfusius » Fri Oct 19, 2012 8:42 am

Eval can be useful if you want to write your own repl or interpreter. But in 99% of the cases where beginners tend to use eval there is a better solution using functional objects and funcall. It also executes much faster than eval.

Code: Select all

(setf z 666)
(let ((z 14))
    (print z)
    (funcall #'(lambda () (setf www z))))

sylwester
Posts: 133
Joined: Mon Jul 11, 2011 2:53 pm

Re: Behaviour of EVAL inside LET

Post by sylwester » Fri Oct 19, 2012 4:56 pm

The hyperspec says it evaluates the expression in the current dynamic environment, but the null lexical environment.

Thus:

Code: Select all

(defparameter z 666) ; z is dynamicly scoped 
(let ((z 14)) 
  (eval '(setf www z)))

www
==> 14
Since z is dynamicly scoped and not lexical.
So what let is shadowing is important.
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p

abvgdeika
Posts: 20
Joined: Mon Jun 06, 2011 10:59 pm

Re: Behaviour of EVAL inside LET

Post by abvgdeika » Tue Oct 30, 2012 2:38 pm

Thanks for all answers!
I still have some problems with lexical closures, now even without "eval" operator.
When executing the following code

Code: Select all

(setf a 666)
(setf command '(+ a 1) )
  
(let ((a 13))
  (funcall    (list 'lambda () command)     )   
)
the result of the last "let" construction is 667 but not 14 as expected.
My question: is there any clever way to "execute" the variable COMMAND inside "let" constiruction
so that this execution uses the current value of variable "A" which is 13?
To understand LISP, you must first understand LISP.

sylwester
Posts: 133
Joined: Mon Jul 11, 2011 2:53 pm

Re: Behaviour of EVAL inside LET

Post by sylwester » Tue Oct 30, 2012 3:45 pm

(list 'lambda () command) returns a lambda-expression not a evaluated lambda-expression so this won't work in many CL-implementations.
As mentioned before; If you declare a dynamically scoped it will do what you want.

Code: Select all

(defparameter a 666)
(setf command (lambda () (+ a 1)))
  
(let ((a 13))
  (funcall command) ==> 14
)

;; OR
(defparameter a 666)
(defun command () (+ a 1))
  
(let ((a 13))
  (command) ==> 14
)
I'm the author of two useless languages that uses BF as target machine.
Currently I'm planning a Scheme compiler :p

abvgdeika
Posts: 20
Joined: Mon Jun 06, 2011 10:59 pm

Re: Behaviour of EVAL inside LET

Post by abvgdeika » Thu Nov 15, 2012 3:31 pm

After some experiments I found a right replacement of EVAL operator which works as expected inside lexical closures. This is the following macro:

Code: Select all

(defmacro true-eval (command-expression)
  `(macrolet ((evaluate () ,command-expression))
     (evaluate)
     ))
Here is a testing code:

Code: Select all

(setf command '(+ c j) )
(setf c 666)
(loop for j from 1 to 3 do
      (let ((c -13))
        (print (true-eval command) )
        ) )
Resulting printed sequence of values is -12 -11 -10 as expected!
Even a nested call of "true-eval" works:

Code: Select all

(setf inner-command '(+ c j) )
(setf outer-command '(* 10 (true-eval inner-command))  )
(setf c 666)
(loop for j from 1 to 3 do
      (let ((c -13))
        (print (true-eval outer-command) )
        )
      )
Resulting printed sequence is -120 -110 -100 as expected!
To understand LISP, you must first understand LISP.

Goheeca
Posts: 271
Joined: Thu May 10, 2012 12:54 pm
Contact:

Re: Behaviour of EVAL inside LET

Post by Goheeca » Fri Nov 16, 2012 4:47 am

Why don't you use simply quasiquotation in this case?

Code: Select all

(loop for i below 10 do (print (eval `(+ 20 ,i))))
Because true-eval isn't really a right replacement, let me show it:

Code: Select all

(loop for i below 3 do (format t "~a> ~a~%" i (eval (read))))
vs.

Code: Select all

(loop for i below 3 do (format t "~a> ~a~%" i (true-eval (read))))
The macroexpansion works during the compile time.
// Add finish-output if it's needed.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version). Temporary mirrors of aferomentioned: CLHS and a dark version.

Post Reply