I know that Lisp and Scheme programmers usually say that eval
should be avoided unless strictly necessary. I’ve seen the same recommendation for several program
Like the GOTO "rule": If you don't know what you are doing, you can make a mess.
Besides from only building something out of known and safe data, there's the problem that some languages/implementations can't optimize the code enough. You could end up with interpreted code inside eval
.
There are several reasons why one should not use EVAL
.
The main reason for beginners is: you don't need it.
Example (assuming Common Lisp):
EVALuate an expression with different operators:
(let ((ops '(+ *)))
(dolist (op ops)
(print (eval (list op 1 2 3)))))
That's better written as:
(let ((ops '(+ *)))
(dolist (op ops)
(print (funcall op 1 2 3))))
There are lots of examples where beginners learning Lisp think they need EVAL
, but they don't need it - since expressions are evaluated and one can also evaluate the function part. Most of the time the use of EVAL
shows a lack of understanding of the evaluator.
It is the same problem with macros. Often beginners write macros, where they should write functions - not understanding what macros are really for and not understanding that a function already does the job.
It often is the wrong tool for the job to use EVAL
and it often indicates that the beginner does not understand the usual Lisp evaluation rules.
If you think you need EVAL
, then check if something like FUNCALL
, REDUCE
or APPLY
could be used instead.
FUNCALL
- call a function with arguments: (funcall '+ 1 2 3)
REDUCE
- call a function on a list of values and combine the results: (reduce '+ '(1 2 3))
APPLY
- call a function with a list as the arguments: (apply '+ '(1 2 3))
.Q: do I really need eval or does the compiler/evaluator already what I really want?
The main reasons to avoid EVAL
for slightly more advanced users:
you want to make sure that your code is compiled, because the compiler can check code for many problems and generates faster code, sometimes MUCH MUCH MUCH (that's factor 1000 ;-) )faster code
code that's constructed and needs to be evaluated can't be compiled as early as possible.
eval of arbitrary user input opens up security problems
some use of evaluation with EVAL
can happen at the wrong time and create build problems
To explain the last point with a simplified example:
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
So, I may want to write a macro that based on the first parameter uses either SIN
or COS
.
(foo 3 4)
does (sin 4)
and (foo 1 4)
does (cos 4)
.
Now we may have:
(foo (+ 2 1) 4)
This does not give the desired result.
One then may want to repair the macro FOO
by EVALuating the variable:
(defmacro foo (a b)
(list (if (eql (eval a) 3) 'sin 'cos) b))
(foo (+ 2 1) 4)
But then this still does not work:
(defun bar (a b)
(foo a b))
The value of the variable is just not known at compile time.
A general important reason to avoid EVAL
: it is often used for ugly hacks.
Eval is not evil. Eval is not complicated. It is a function that compiles the list you pass to it. In most other languages, compiling arbitrary code would mean learning the language's AST and digging around in the compiler internals to figure out the compiler API. In lisp, you just call eval.
When should you use it? Whenever you need to compile something, typically a program that accepts, generates or modifies arbitrary code at runtime.
When shouldn't you use it? All other cases.
Why shouldn't you use it when you don't need to? Because you would be doing something in an unnecessarily complicated way that may cause problems for readability, performance and debugging.
Yeah, but if I'm a beginner how do I know if I should use it? Always try to implement what you need with functions. If that doesn't work, add macros. If that still doesn't work, then eval!
Follow these rules and you'll never do evil with eval :)
I like Zak's answer very much and he has gotten at the essence of the matter: eval is used when you are writing a new language, a script, or modification of a language. He doesn't really explain further so I will give an example:
(eval (read-line))
In this simple Lisp program, the user is prompted for input and then whatever they enter is evaluated. For this to work the entire set of symbol definitions has to be present if the program is compiled, because you have no idea which functions the user might enter, so you have to include them all. That means that if you compile this simple program, the resulting binary will be gigantic.
As a matter of principle, you can't even consider this a compilable statement for this reason. In general, once you use eval, you are operating in an interpreted environment, and the code can no longer be compiled. If you do not use eval then you can compile a Lisp or Scheme program just like a C program. Therefore, you want to make sure you want and need to be in an interpreted environment before committing to using eval.
Eval is fine, as long as you know EXACTLY what is going into it. Any user input going into it MUST be checked and validated and everything. If you don't know how to be 100% sure, then don't do it.
Basically, a user can type in any code for the language in question, and it will execute. You can imagine for yourself how much damage he can do.
The canonical answer is to stay away. Which I find weird, because it's a primitive, and of the seven primitives (the others being cons, car, cdr, if, eq and quote), it gets far and away the least amount of use and love.
From On Lisp: "Usually, calling eval explicitly is like buying something in an airport gift-shop. Having waited till the last moment, you have to pay high prices for a limited selection of second-rate goods."
So when do I use eval? One normal use is to have an REPL within your REPL by evaluating (loop (print (eval (read))))
. Everyone is fine with that use.
But you can also define functions in terms of macros that will be evaluated after compilation by combining eval with backquote. You go
(eval `(macro ,arg0 ,arg1 ,arg2))))
and it will kill the context for you.
Swank (for emacs slime) is full of these cases. They look like this:
(defun toggle-trace-aux (fspec &rest args)
(cond ((member fspec (eval '(trace)) :test #'equal)
(eval `(untrace ,fspec))
(format nil "~S is now untraced." fspec))
(t
(eval `(trace ,@(if args `(:encapsulate nil) (list)) ,fspec ,@args))
(format nil "~S is now traced." fspec))))
I don't think it's a filthy hack. I use it all the time myself to reintegrate macros into functions.