Question

user=> (eval '(+ 1 2))
3
user=> (eval '('+ 1 2))
2
user=> (eval (list '+ '1 '2))
3
user=> (eval (list + '1 '2))
3

Did that mean '+ is sometimes the same from + ?

Is there any rule to determine what situation should be considered?

Was it helpful?

Solution

(list '+ '1 '2) produces a list of the symbol + and the numbers 1 and 2. Evaluating that will look up the function named by the symbol + and call it with arguments 1 and 2.

(list + '1 '2) produces a list of the function named by the symbol + and the numbers 1 and 2. Evaluating that will call the function that's already directly an element of the list with the arguments from the rest of the list.

There's never a need to quote numbers, by the way. They're self-evaluating.

EDIT:

The more interesting case which you didn't address in your headline is what happens in the (eval '('+ 1 2)) case? Why does this not return 3 like the other cases that all end up calling the function named by + with the arguments 1 and 2?

The answer is that it's different because this time, you have quoted the symbol + twice. Because of that, the symbol doesn't get resolved to the function it names, instead the symbol itself is called as a function.

Now what is the behaviour when a symbol gets called as a function? It treats its first argument as a map and looks itself up as a key in that map. But the number 1 is obviously not a map, so it would now usually return nil to indicate that no matching key was found.

In this case though, there's a second argument to the call to the symbol +, and that second argument gets used as the default value to return when no matching key was found. So that's why the form (eval '('+ 1 2)) returns 2.

EDIT 2:

OK, and now to finally answer the question: why does quoting a list produce different results than calling the list function with the same arguments. That's because a quote ' stops all evaluation on the list it's used on, but the list function evaluates its arguments before producing a list with them as elements.

These differences all becomes very clear when you print the various forms instead of sending them directly to eval:

user> '(+ 1 2)
(+ 1 2)
user> '('+ 1 2)
((quote +) 1 2)
user> (list '+ '1 '2)
(+ 1 2)
user> (list + '1 '2)
(#<core$_PLUS_ clojure.core$_PLUS_@165c64> 1 2)

You see that the first and third case produce a result that prints the same, because in both these cases you've quoted + once. In the second case, + is quoted twice instead, and in the last it isn't quoted at all.

EDIT 3:

One last addition that should hopefully clarify things even further: unless its first element names a macro or special operator, eval will recursively evaluate all elements of a list (including the first) before evaluating the list itself by calling its (evaluated) first argument as a function with the rest of the list as arguments.

If that first element is '+ or (quote +) (these are two different ways to write the same thing, the quoted symbol +), it will evaluate to the symbol + and that will be called as function.

If the first element is +, that will evaluate to the the actual addition function (clojure.core$_PLUS_). If it already is that function, it will stay the same (basically all types besides lists and symbols are self-evaluating). In both cases, eval will then call that function.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top