summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Halloway <stu@thinkrelevance.com>2010-04-23 13:47:44 -0400
committerStuart Halloway <stu@thinkrelevance.com>2010-04-24 14:28:40 -0400
commite0e0b6a2f192bf743e3629dff0b23a39ccf4f8db (patch)
tree8e48cd72ce7b52445bdfd79db5e19ed14d8d76e8
parentd5578ae81ee920341a62d10f66744600ea832c48 (diff)
make defrecord .cons work, #231
- based on original patch from Allen Rohner - altered to handle nil correctly - added test cases Signed-off-by: Stuart Halloway <stu@thinkrelevance.com>
-rw-r--r--src/clj/clojure/core_deftype.clj18
-rw-r--r--test/clojure/test_clojure/protocols.clj24
2 files changed, 41 insertions, 1 deletions
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj
index ce81b448..6e1d8d7e 100644
--- a/src/clj/clojure/core_deftype.clj
+++ b/src/clj/clojure/core_deftype.clj
@@ -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]
@@ -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)]
diff --git a/test/clojure/test_clojure/protocols.clj b/test/clojure/test_clojure/protocols.clj
index b38f41cd..7f678234 100644
--- a/test/clojure/test_clojure/protocols.clj
+++ b/test/clojure/test_clojure/protocols.clj
@@ -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?
@@ -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
+