DevKev
DevKev

Reputation: 79

clojure.lang.Cons cannot be cast to Clojure.lang.Ifn

Hi everybody I've been recently trying to learn to a new language and I sort of bumped into Clojure which look like a real interesting language because I've never heard about functional programming, even though I had used JavaScript before that kind of leverages it, well I'm gonna stop with the small talk and get into the problem.

I've been working on solving the https://github.com/gigasquid/wonderland-clojure-katas and more specific on the doublets problem. I think I have came with a solution but it send me the error on the title of this post. I have read about this error and it seems that it triggers when you want the compiler expects a function but it doesn't. Here is the full code of my solution to see if you can help me out with this one:

(ns doublets.solver
  (:require [clojure.java.io :as io]
            [clojure.edn :as edn]
            [clojure.set :as set]))

(def words (-> "words.edn"
               (io/resource)
               (slurp)
               (read-string)))

(defn linked-word [word word-list]
  (some #(when (= (count (set/difference (into #{} (seq %))
                                         (into #{} (seq word)))) 1) %)
        word-list))

(defn doublets [word1 word2]
  (let [n (count word1) v (cons word1 (filter #(= (count %) n)
                              (remove #{word1} words)))]
    (tree-seq #(and (linked-word (% 0) %) (not= (% 0) word2))
              #(cons (linked-word (% 0) (rest %))
                (remove #{(% 0)} (rest %))) v)))

As you can see cons is a function so the error doesn't seem to be the case described above.

Upvotes: 1

Views: 2107

Answers (1)

Alan Thompson
Alan Thompson

Reputation: 29958

I can reproduce the error after downloading the words.edn file and running with (doublets "bank" "loan"). I think the problem is these expressions:

 (% 0)

which you have in a few places. I see that you are cons-ing some things, so that may be a clue. What is (% 0) supposed to do? If you want the first char, just say (first xyz) or something.

I would also break out the anonymous functions #(...) and give them real names.

Update

My guess seems to be correct as this experiment shows:

(cons 1 [2 3]) => (1 2 3)
(class (cons 1 [2 3])) => clojure.lang.Cons

(vec (cons 1 [2 3])) => [1 2 3]
(class (vec (cons 1 [2 3]))) => clojure.lang.PersistentVector

OK, rewrite like:

(defn doublets [word1 word2]
  (let [n (count word1)
        v (vec (cons word1 (filter #(= (count %) n)
                             (remove #{word1} words))))]
    (tree-seq
      #(and
         (linked-word (% 0) %)
         (not= (% 0) word2))
      #(vec (cons (linked-word (% 0) (rest %)))
         (remove #{(% 0)} (rest %)))
      v)))

new error: java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol

And there is the clue we needed!

We are evaluating things as symbols, not strings! The problem is read-string, which is how you read source code, not data like strings. Delete read-string:

(def words (-> "words.edn"
             (io/resource)
             (slurp)))

We now get a new error on this line:

    v (vec (cons word1 (filter #(= (count %) n)
                         (remove #{word1} words))))]

ERROR in (dotest-line-40) (RT.java:664)
Uncaught exception, not in assertion.
expected: nil
  actual: java.lang.UnsupportedOperationException: 
             count not supported on this type: Character

So your seq has created something like "foo" => [\f \o \o], and you then try to say (count \f). You can't count a single char, only a string.

I'll let you debug it from there.

Upvotes: 4

Related Questions