Search code examples
clojure

Passing a list of functions to 'map' in clojure gives nil


I am trying out a few examples of clojure.

(def sum #(reduce + %))

(def avg #(/ (sum %) (count %)))

(defn stats
  [numbers]
  (map #(% numbers) '(sum, avg)) ;;works when it is [sum avg]
  )

When I call stats function

 (stats [1 24  235 34511 0 14])

it returns (nil nil). But if I change the code as mentioned in the comment it returns the expected output.

(34785 11595/2)

Why can't functions be passed as a list?


Solution

  • You can pass the functions in the collection argument to map, but the ' prefix in your example is quoting the list, so the contents are the symbols sum and avg instead of the values.

    '(sum avg)     ;; quoted list, contents are symbols
    '[sum avg]     ;; quoted vector, contents are symbols
    (list sum avg) ;; list of the functions, using `list` fn to create a list
    [sum avg]      ;; vector of the functions
    

    ' is a shorthand for quote.

    Unquoted list literals are treated specially. Clojure interprets an unquoted list literal as an invocation, where the first element in the list refers to what's being invoked. For example, this would invoke the sum function, passing the avg function as its first argument (which won't work):

    (sum avg)
    

    By mapping the type function over quoted and non-quoted lists, we can see the difference in types of the list elements:

    user=> (map type '(conj assoc))
    (clojure.lang.Symbol clojure.lang.Symbol)          ;; symbols
    user=> (map type (list conj assoc))
    (clojure.core$conj__5112 clojure.core$assoc__5138) ;; fn values
    

    Here's another extensive answer on quoting: Using quote in Clojure