Skip to content

Commit

Permalink
make defrecord .cons work, #231
Browse files Browse the repository at this point in the history
- based on original patch from Allen Rohner
  - altered to handle nil correctly
  - added test cases

Signed-off-by: Stuart Halloway <stu@thinkrelevance.com>
  • Loading branch information
stuarthalloway committed Apr 24, 2010
1 parent d5578ae commit e0e0b6a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 1 deletion.
18 changes: 17 additions & 1 deletion src/clj/clojure/core_deftype.clj
Expand Up @@ -107,6 +107,22 @@
(defn munge [s]
((if (symbol? s) symbol str) (clojure.lang.Compiler/munge (str s))))

(defn- imap-cons
[#^IPersistentMap this o]
(cond
(instance? java.util.Map$Entry o)
(let [#^java.util.Map$Entry pair o]
(.assoc this (.getKey pair) (.getValue pair)))
(instance? clojure.lang.IPersistentVector o)
(let [#^clojure.lang.IPersistentVector vec o]
(.assoc this (.nth vec 0) (.nth vec 1)))
:else (loop [this this
o o]
(if (seq o)
(let [#^java.util.Map$Entry pair (first o)]
(recur (.assoc this (.getKey pair) (.getValue pair)) (rest o)))
this))))

(defn- emit-defrecord
"Do not use this directly - use defrecord"
[tagname name fields interfaces methods]
Expand Down Expand Up @@ -163,7 +179,7 @@
(conj m
`(count [~'this] (+ ~(count base-fields) (count ~'__extmap)))
`(empty [~'this] (throw (UnsupportedOperationException. (str "Can't create empty: " ~(str classname)))))
`(cons [~'this ~'e] (let [[~'k ~'v] ~'e] (.assoc ~'this ~'k ~'v)))
`(cons [~'this ~'e] ((var imap-cons) ~'this ~'e))
`(equiv [~'this ~'o] (.equals ~'this ~'o))
`(containsKey [~'this ~'k] (not (identical? ~'this (.valAt ~'this ~'k ~'this))))
`(entryAt [~'this ~'k] (let [~'v (.valAt ~'this ~'k ~'this)]
Expand Down
24 changes: 24 additions & 0 deletions test/clojure/test_clojure/protocols.clj
Expand Up @@ -30,6 +30,15 @@
(map #(.getName %))
(sort)))

(defrecord TestRecord [a b])
(defn r
([a b] (TestRecord. a b))
([a b meta ext] (TestRecord. a b meta ext)))
(defrecord MapEntry [k v]
java.util.Map$Entry
(getKey [_] k)
(getValue [_] v))

(deftest protocols-test
(testing "protocol fns throw IllegalArgumentException if no impl matches"
(is (thrown-with-msg?
Expand Down Expand Up @@ -143,9 +152,24 @@
(is (= (.hashCode (DefrecordObjectMethodsWidgetB. 1)) (.hashCode (DefrecordObjectMethodsWidgetB. 1))))
(is (not= (.hashCode (DefrecordObjectMethodsWidgetA. 1)) (.hashCode (DefrecordObjectMethodsWidgetB. 1))))))

(deftest defrecord-acts-like-a-map
(let [rec (r 1 2)]
(is (= (r 1 3 {} {:c 4}) (merge rec {:b 3 :c 4})))))

(deftest defrecord-interfaces-test
(testing "IPersistentCollection"
(testing ".cons"
(let [rec (r 1 2)]
(are [x] (= rec (.cons rec x))
nil {})
(is (= (r 1 3) (.cons rec {:b 3})))
(is (= (r 1 4) (.cons rec [:b 4])))
(is (= (r 1 5) (.cons rec (MapEntry. :b 5))))))))

;; todo
;; what happens if you extend after implementing directly? Extend is ignored!!
;; extend-type extend-protocol extend-class
;; maybe: find-protocol-impl find-protocol-method
;; deftype, printable forms
;; reify, definterface

0 comments on commit e0e0b6a

Please sign in to comment.