Search code examples
clojure

Why will Clojure accept this when entered into a REPL, but not when written as a function?


I have been writing a fairly simple piece of code to get the hang of Clojure and I've run into an issue where when I pass each line into a REPL in order (while using a test case to substitute the values that would be passed as part of the function), I get the expected result but when I try to compile it as a function I get the error Execution error (ClassCastException) at testenv.core/sum-of-two-largest-squares (core.clj:14). class clojure.lang.PersistentList cannot be cast to class clojure.lang.IFn (clojure.lang.PersistentList and clojure.lang.IFn are in unnamed module of loader 'app') The relevant function is as follows (note that I've moved each step into variables in order to figure out the problem)

(defn sum-of-two-largest-squares [a b c]
     (
      (def x (into () (map math/abs [a b c])))
      (def m (apply min x))
      (def y (into () (map square (remove (fn [n] (= n m)) x))))
      (apply + y)
     )
)

Solution

  • You can't just put parenthesis around things without expecting it to change the meaning.

    What works when run in a REPL is:

    (defn abs [n] (java.lang.Math/abs n))
    (defn square [n] (* n n))
    
    (def x (into () (map abs [a b c])))
    (def m (apply min x))
    (def y (into () (map square (remove (fn [n] (= n m)) x))))
    (apply + y)
    

    ...and strictly, this still works if injected into a function, as long as you take out the extra parenthesis (though it works slowly and with unwanted side effects due to the inappropriate use of def):

    (defn sum-of-two-largest-squares [a b c]
      (def x (into () (map abs [a b c])))
      (def m (apply min x))
      (def y (into () (map square (remove (fn [n] (= n m)) x))))
      (apply + y)
    )
    (sum-of-two-largest-squares a b c)
    

    However, a good-practice alternative would use let instead:

    (defn abs [n] (java.lang.Math/abs n))
    (defn square [n] (* n n))
    
    (defn sum-of-two-largest-squares [a b c]
         (let [x (into () (map abs [a b c]))
               m (apply min x)
               y (into () (map square (remove (fn [n] (= n m)) x)))]
           (apply + y)))