Search code examples
clojurerefactoringanonymous-function

Refactoring clojure `defn` to `fn` causes ClassCastException


I'm working on 4clojure's Prime Numbers kata. I have solution that works perfectly (if inefficiently) on my local machine, but it relies on defn which is forbidden on 4clojure.

(defn factors [n]
  (->> n
       range
       (map inc)
       (filter #(zero? (mod n %)))))

(defn prime? [n]
  (->> (factors n)
       (count)
       (= 2)))

(defn n-primes [n]
  (->> (range)
       (filter prime?)
       (take n)))

I tried to refactor it to use only anonymous functions, but it blows up.

(fn x-primes [count]
  (let [x-factors (fn factors [n] (->> n range (map inc) (filter #(zero? (mod n %)))))
         x-prime? (fn [n] (->> (x-factors n) (count) (= 2)))]
        (->> (range)
             (filter x-prime?)
             (take count))))

Throwing the following error (when run both locally and with 4clojure's editor)

ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn

What have I done wrong in refactoring the code?

On an aside, why do you suppose 4clojure bans def and defn?


Solution

  • You're shadowing the count function with your parameter name. (count some-num) is throwing an error because in this scope, count is a number, not a function.

    Change your parameter name from count to something else.


    Also, in 4Clojure's help page, there's the following line:

    Some operations are prohibited for security reasons. For instance, you will not be able to use "def" or switch namespaces.

    Since defn is just a def wrapping macro, it makes sense that it's also prohibited. How that would lead to a security problem though is beyond me.