zcaudate
zcaudate

Reputation: 14258

Strange error when using atoms inside deftype

I have the following code, defining a type that has an atom in there.

(defprotocol IDeck
  (vec-* [dk] "Output to a persistent vector")
  (count-* [dk] "Number of elements in the deck")
  (conj1-*  [dk & es] "Adding multiple elements to the deck"))


(deftype ADeck [#^clojure.lang.Atom val]
  IDeck
  (vec-* [dk] (->> (.val dk) deref (map deref) vec))

  (count-* [dk] (-> (.val dk) deref count))

  (conj1-* [dk & es]
    (try
      (loop [esi es]
        (let [e (first esi)]
          (cond
            (nil? e) dk
            :else
            (do
              (swap! (.val dk) #(conj % (atom e)))
              (recur (rest esi))))))
      (catch Throwable t (println t)))))

(defn new-*adeck
  ([] (ADeck. (atom [])))
  ([v] (ADeck. (atom (vec (map atom v))))))

(defn conj2-* [dk & es]
  (try
    (loop [esi es]
      (let [e (first esi)]
        (cond
          (nil? e) dk
          :else
          (do
            (swap! (.val dk) #(conj % (atom e)))
            (recur (rest esi))))))
    (catch Throwable t (println t))))


;; Usage 
(def a (new-*adeck [1 2 3 4]))

(count-* a)
;=> 4

(vec-* a)
;=> [1 2 3 4]

(conj1-* a 1 2)  ;; The deftype case
;=> IllegalArgumentException java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long

(vec-* a)
;=> [1 2 3 4]

(conj2-* a 1 2) ;; The defn case
(vec-* a)
;=> [1 2 3 4 1 2]

Even though the two conj-* methods are exactly the same, except that one is in a deftype and the other is a normal defn, the first gives an error while the second succeeds. Why is this?

Upvotes: 2

Views: 183

Answers (1)

Ankur
Ankur

Reputation: 33637

This is because protocols doesn't support variable number of arguments.

What you can do is make:

(conj1-*  [dk & es] "Adding multiple elements to the deck"))

into

(conj1-*  [dk es] "Adding multiple elements to the deck"))

such that the es param will be vector and called like:

(conj1-* a [1 2])

Upvotes: 2

Related Questions