Search code examples
clojurefunctional-programmingmacrossymbols

Clojure - how exactly symbols are evaluated?


We know clojure evaluates (+ 1 1) in couple of stages from reading the unicode characters to actual evaluation of the reader output...

In code, that looks something like
(eval (read-string "(+ 1 1)"))

However it hurts my brain when ('+ '1 '2) (or ('+ 1 2)) gives me 2....

core> (def something ('+ '1 '2))
#'alcamii4hr.core/something
core> (type something)
java.lang.Long


Solution

  • Symbols, like keywords, can be invoked like functions. When invoked, they both do the same thing, look themselves up in what they presume to be a map. They have two arities [a-map] and [a-map not-found].

    ;; not-found defaults to nil
    ('foo {'bar 42}) ;=> nil
    
    ('foo {'foo 42}) ;=> 42
    
    ;; not-found can also be supplied in the arity-2 case
    ('foo {'bar 42} :not-found) ;=> :not-found
    

    In your specific case, it just assumes that the first argument is something associative, and doesn't check. It can't find the symbol + in what it presumes to be a map, so it returns the not-found value: 2.

    ('+ '1) ;=> nil     ; not-found
    
    ('+ '1 '2) ;=> 2    ; because 2 is the not-found values
    
    ('+ '1 '2 '3) ;=> clojure.lang.ArityException
    

    To see how this all fits together, you can check the invoke method on the symbol class, which calls clojure.RT.get, which is what looks something up in an associative thing. This is the same method called by clojure.core/get.

    In clourescript, this works the same way, but is implemented differently. (In clojurescript itself, and not in the host language.) You can read about that here.