2

Why does the expression

('+ 10 20)

evaluate to 20? This is realy bizarre to me! I would have expected to give an error, because the first element of the list, '+ i not a thing that can be evaluated!

Siddharth Bhat
  • 823
  • 5
  • 15

1 Answers1

8

This is pretty much an FAQ with Clojure, for good or bad. So '+ is shorthand for (quote +) which evaluates to the symbol +. Symbols (and keywords) can be treated as functions and look themselves up in their argument, so ('+ 10 20) is essentially (get 10 '+ 20) and that's the not-found arity of get so if the "collection" 10 does not include the symbol '+ then it will return the third argument, 20.

You might expect associative lookup against 10 to fail and throw an exception but there are a lot of situations in Clojure where lookup will return nil (or the not-found version) because it's beneficial from a nil-punning point of view and is part of what leads to idiomatic Clojure.

Some functions do perform that sort of argument checking: (contains? 10 '+) will throw an exception, for example, whereas both (get 10 '+) and (get '+ 10) will both return nil.

It's probably also worth noting that the following throws an exception:

user=> ('+ 10 20 30)
Execution error (ArityException) at user/eval39476 (REPL:1).
Wrong number of args (3) passed to: clojure.lang.Symbol

You asked if the "value of a list with quoted first argument in clojure is the last argument" and the answer is no, this is very specifically a two-argument function invocation, because of the above logic: you can't "call" a symbol with 3 args, only with 1 or 2.

Worth noting the difference here for the 0 args case:

user=> ('+)
Execution error (ArityException) at user/eval39478 (REPL:1).
Wrong number of args (0) passed to: clojure.lang.Symbol
user=> (+)
0

The first case is a symbol lookup -- which requires at least a collection to look itself up in -- whereas the second case is a function invocation (of clojure.core/+) with zero arguments, which returns the identity value of + in the same way that (*) returns 1.

Sean Corfield
  • 6,297
  • 22
  • 31