Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions persistent.carp
Original file line number Diff line number Diff line change
Expand Up @@ -1788,6 +1788,34 @@ This generates:
(defn values [map-ref]
(reduce &(fn [acc p] (Array.push-back acc @(Pair.b &p))) [] map-ref))

(doc merge
"Merge two maps. On key collision the second (right) map wins.")
(sig merge (Fn [(Ref %name a) (Ref %name b)] %name))
(defn merge [a-ref b-ref]
(reduce
&(fn [acc p] (insert @(Pair.a &p) @(Pair.b &p) &acc))
@a-ref
b-ref))

(doc merge-with
"Merge two maps with a conflict resolver. For keys in both maps, call the resolver with both values.")
(sig merge-with
(Fn
[(Ref (Fn [%value-type %value-type] %value-type) q)
(Ref %name a)
(Ref %name b)]
%name))
(defn merge-with [f a-ref b-ref]
(reduce
&(fn [acc p]
(let [k @(Pair.a &p)
bv @(Pair.b &p)]
(match (get @&k &acc)
(Maybe.Nothing) (insert k bv &acc)
(Maybe.Just av) (insert k (~f av bv) &acc))))
@a-ref
b-ref))

(doc =
"Structural equality: same size and pairwise equal entries in ascending key order.")
(sig = (Fn [(Ref %name q) (Ref %name q)] Bool))
Expand Down Expand Up @@ -2404,6 +2432,34 @@ Example:
(defn values [map-ref]
(reduce &(fn [acc p] (Array.push-back acc @(Pair.b &p))) [] map-ref))

(doc merge
"Merge two maps. On key collision the second (right) map wins.")
(sig merge (Fn [(Ref %name a) (Ref %name b)] %name))
(defn merge [a-ref b-ref]
(reduce
&(fn [acc p] (insert @(Pair.a &p) @(Pair.b &p) &acc))
@a-ref
b-ref))

(doc merge-with
"Merge two maps with a conflict resolver. For keys in both maps, call the resolver with both values.")
(sig merge-with
(Fn
[(Ref (Fn [%value-type %value-type] %value-type) q)
(Ref %name a)
(Ref %name b)]
%name))
(defn merge-with [f a-ref b-ref]
(reduce
&(fn [acc p]
(let [k @(Pair.a &p)
bv @(Pair.b &p)]
(match (get &k &acc)
(Maybe.Nothing) (insert k bv &acc)
(Maybe.Just av) (insert k (~f av bv) &acc))))
@a-ref
b-ref))

(doc =
"Structural equality: same size and same set of `(key, value)` entries.")
(sig = (Fn [(Ref %name q) (Ref %name q)] Bool))
Expand Down
73 changes: 73 additions & 0 deletions test/persistent_hash_map.carp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,79 @@
(and (= &a &b) (not (= &a &c))))
"= is structural regardless of insertion order")

(assert-equal test
true
(let [a (IntIntMap.insert 1 10 &(IntIntMap.insert 2 20 &(IntIntMap.empty)))
b (IntIntMap.insert 3 30 &(IntIntMap.insert 4 40 &(IntIntMap.empty)))
m (IntIntMap.merge &a &b)]
(and (= (IntIntMap.length &m) 4l)
(let [k1 1
k3 3]
(and
(match (IntIntMap.get &k1 &m)
(Maybe.Just v) (= v 10)
(Maybe.Nothing) false)
(match (IntIntMap.get &k3 &m)
(Maybe.Just v) (= v 30)
(Maybe.Nothing) false)))))
"merge of disjoint maps contains all entries")

(assert-equal test
true
(let [a (IntIntMap.insert 1 10 &(IntIntMap.insert 2 20 &(IntIntMap.empty)))
b (IntIntMap.insert 2 99 &(IntIntMap.insert 3 30 &(IntIntMap.empty)))
m (IntIntMap.merge &a &b)
k 2]
(and (= (IntIntMap.length &m) 3l)
(match (IntIntMap.get &k &m)
(Maybe.Just v) (= v 99)
(Maybe.Nothing) false)))
"merge right wins on key collision")

(assert-equal test
true
(let [a (IntIntMap.insert 1 10 &(IntIntMap.insert 2 20 &(IntIntMap.empty)))
e (IntIntMap.empty)
m1 (IntIntMap.merge &a &e)
m2 (IntIntMap.merge &e &a)]
(and (= &m1 &a) (= &m2 &a)))
"merge with empty is identity")

(assert-equal test
true
(let [a (IntIntMap.insert 1 10 &(IntIntMap.insert 2 20 &(IntIntMap.empty)))
b (IntIntMap.insert 2 3 &(IntIntMap.insert 3 30 &(IntIntMap.empty)))
m (IntIntMap.merge-with &(fn [x y] (+ x y)) &a &b)
k1 1
k2 2
k3 3]
(and (= (IntIntMap.length &m) 3l)
(match (IntIntMap.get &k1 &m)
(Maybe.Just v) (= v 10)
(Maybe.Nothing) false)
(match (IntIntMap.get &k2 &m)
(Maybe.Just v) (= v 23)
(Maybe.Nothing) false)
(match (IntIntMap.get &k3 &m)
(Maybe.Just v) (= v 30)
(Maybe.Nothing) false)))
"merge-with addition combiner on overlapping keys")

(assert-equal test
true
(let [a (IntIntMap.insert 1 10 &(IntIntMap.empty))
b (IntIntMap.insert 2 20 &(IntIntMap.empty))
m (IntIntMap.merge-with &(fn [x y] (+ x y)) &a &b)
k1 1
k2 2]
(and (= (IntIntMap.length &m) 2l)
(match (IntIntMap.get &k1 &m)
(Maybe.Just v) (= v 10)
(Maybe.Nothing) false)
(match (IntIntMap.get &k2 &m)
(Maybe.Just v) (= v 20)
(Maybe.Nothing) false)))
"merge-with on disjoint maps just takes values")
(assert-memory-balance test
map-branch-lifecycle
0l
Expand Down
77 changes: 77 additions & 0 deletions test/persistent_ord_map.carp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,83 @@
(= &filtered &m2))
"filter with always-true preserves all entries")

(assert-equal test
true
(let [a (IntIntOMap.insert 1
10
&(IntIntOMap.insert 2 20 &(IntIntOMap.empty)))
b (IntIntOMap.insert 3
30
&(IntIntOMap.insert 4 40 &(IntIntOMap.empty)))
m (IntIntOMap.merge &a &b)]
(and (= (IntIntOMap.length &m) 4l)
(match (IntIntOMap.get 1 &m)
(Maybe.Just v) (= v 10)
(Maybe.Nothing) false)
(match (IntIntOMap.get 3 &m)
(Maybe.Just v) (= v 30)
(Maybe.Nothing) false)))
"merge of disjoint maps contains all entries")

(assert-equal test
true
(let [a (IntIntOMap.insert 1
10
&(IntIntOMap.insert 2 20 &(IntIntOMap.empty)))
b (IntIntOMap.insert 2
99
&(IntIntOMap.insert 3 30 &(IntIntOMap.empty)))
m (IntIntOMap.merge &a &b)]
(and (= (IntIntOMap.length &m) 3l)
(match (IntIntOMap.get 2 &m)
(Maybe.Just v) (= v 99)
(Maybe.Nothing) false)))
"merge right wins on key collision")

(assert-equal test
true
(let [a (IntIntOMap.insert 1
10
&(IntIntOMap.insert 2 20 &(IntIntOMap.empty)))
e (IntIntOMap.empty)
m1 (IntIntOMap.merge &a &e)
m2 (IntIntOMap.merge &e &a)]
(and (= &m1 &a) (= &m2 &a)))
"merge with empty is identity")

(assert-equal test
true
(let [a (IntIntOMap.insert 1
10
&(IntIntOMap.insert 2 20 &(IntIntOMap.empty)))
b (IntIntOMap.insert 2 3 &(IntIntOMap.insert 3 30 &(IntIntOMap.empty)))
m (IntIntOMap.merge-with &(fn [x y] (+ x y)) &a &b)]
(and (= (IntIntOMap.length &m) 3l)
(match (IntIntOMap.get 1 &m)
(Maybe.Just v) (= v 10)
(Maybe.Nothing) false)
(match (IntIntOMap.get 2 &m)
(Maybe.Just v) (= v 23)
(Maybe.Nothing) false)
(match (IntIntOMap.get 3 &m)
(Maybe.Just v) (= v 30)
(Maybe.Nothing) false)))
"merge-with addition combiner on overlapping keys")

(assert-equal test
true
(let [a (IntIntOMap.insert 1 10 &(IntIntOMap.empty))
b (IntIntOMap.insert 2 20 &(IntIntOMap.empty))
m (IntIntOMap.merge-with &(fn [x y] (+ x y)) &a &b)]
(and (= (IntIntOMap.length &m) 2l)
(match (IntIntOMap.get 1 &m)
(Maybe.Just v) (= v 10)
(Maybe.Nothing) false)
(match (IntIntOMap.get 2 &m)
(Maybe.Just v) (= v 20)
(Maybe.Nothing) false)))
"merge-with on disjoint maps just takes values")

(assert-memory-balance test
map-branch-lifecycle
0l
Expand Down
Loading