Jaxwood
Jaxwood

Reputation: 81

nth not supported on this type: Keyword

I'm new to Clojure and as a learning exercise I'm trying to write a function that validates the present of keys in a map.

when I try to run the code below I get an error saying

java.lang.UnsupportedOperationException: nth not supported on this type: Keyword

(def record {:name "Foobar"})

(def validations [:name (complement nil?)])

(defn validate
  [candidate [key val]]
  (val (get candidate key))

(def actual (every? (partial validate record) validations))
(= true actual)

As I understand I'm partial applying the validate function and asserting every validations function on the map - but it doesn't seem to work - so I must be misunderstanding something?

Upvotes: 0

Views: 2513

Answers (2)

mvarela
mvarela

Reputation: 219

every? takes a collection as the second argument, and so does your validate function. Since you're passing a vector to every?, validate is being called on the contents of the vector (that is, :name and (complement nil?)). You're also missing a closing paren in the definition of validate. Try the following:

(def record {:name "Foobar"})

(def validations [:name (complement nil?)])

(defn validate
  [candidate [key val]]
  (val (get candidate key)))

(def actual (every? (partial validate record) [validations]))
(= true actual)

BTW, you could use some? instead of (complement nil?)

Upvotes: 1

Didier A.
Didier A.

Reputation: 4816

The error is coming from the destructuring that you are using in validate: [key val]. Under the hood, destructuring uses the function nth, and that's what's failing.

Your issue is that you are passing to every? a list of [keyword validation-function]. And every? is iterating over each element of that list and calling the partially applied validate function with it. That means that your validate is called first with the keyword :name and that throws an exception, because you can not extract a [key val] pair out of the keyword :name, causing the exception.

To fix it, you need to make your validations list a list of lists as so:

(def record {:name "Foobar"}) 
(def validations [[:name (complement nil?)]])

(defn validate
  [candidate [key val]]
  (val (get candidate key)))

(def actual (every? (partial validate record) validations))
(= true actual)
;; => true

That way, every? is now iterating over each pair of [keyword validation-function], one at a time, and calling validate with that. Since this is a pair, it can be destructured into a [key val] and everything works.

And just so you know, in newer Clojure versions (1.6 and above), there is now a function called some? which is equivalent to (complement nil?).

Upvotes: 1

Related Questions