<div dir="ltr">My initial reaction to this is that I'd like to see mergeWithKey left alone, there is existing code using it and it continues to be useful, and then to have this new API offered in its own module under Data.Map where the various consumers and providers of WhenMatched and WhenMissing can live and be documented as a coherent toolkit.<br><br><div class="gmail_quote"><div dir="ltr">On Thu, Aug 18, 2016 at 1:08 PM David Feuer <<a href="mailto:david.feuer@gmail.com">david.feuer@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">As I described previously, I've come up with general merge functions<br>
for Data.Map to replace the problematic mergeWithKey (which I desire<br>
to deprecate and remove). I have provisional names for the functions,<br>
and also for the "merge tactics" that go with them and their types. I<br>
would like to get whatever feedback I can before actually adding<br>
these. I'd like to try to make a release including these functions<br>
within approximately two weeks, so prompt responses will be<br>
appreciated.<br>
<br>
Note that the names of the "merge tactics" break with the tradition of<br>
offering plain functions and WithKey versions. To cut down on the<br>
already-large API, only key-passing versions will be provided (unless<br>
people object strongly). This has a small potential efficiency cost,<br>
but it cuts the API in half.<br>
<br>
<br>
The most general function is<br>
<br>
generalMergeA :: (Applicative f, Ord k) => WhenMissing f k a c -><br>
WhenMissing f k b c -> WhenMatched f k a b c -> Map k a -> Map k b -><br>
f (Map k c)<br>
<br>
and the pure version is<br>
<br>
generalMerge :: Ord k => SimpleWhenMissing k a c -> SimpleWhenMissing<br>
k b c -> SimpleWhenMatched k a b c -> Map k a -> Map k b -> Map k c<br>
<br>
where<br>
type SimpleWhenMissing = WhenMissing Identity<br>
type SimpleWhenMatched = WhenMatched Identity<br>
<br>
WhenMissing and WhenMatched are "merge tactics" explaining what to do<br>
with elements in various situations. WhenMissing explains what to do<br>
with keys present in one map but not the other, while WhenMatched<br>
explains what to do with keys present in both maps:<br>
<br>
WhenMissing f k x z is an abstract representation of a function of<br>
type k -> x -> f (Maybe z)<br>
<br>
WhenMatched f k x y z is an abstract representation of a function of<br>
type k -> x -> y -> f (Maybe z)<br>
<br>
So in principle, we have<br>
<br>
generalMergeA ~::~<br>
(Applicative f, Ord k)<br>
=> (k -> a -> f (Maybe c))<br>
-> (k -> b -> f (Maybe c))<br>
-> (k -> a -> b -> f (Maybe c))<br>
-> Map k a -> Map k b -> f (Map k c)<br>
<br>
By using these abstract merge tactics instead of functions, we gain<br>
the ability to perform various optimizations. At present, only the<br>
WhenMissing optimizations are performed (they seem to be the most<br>
important), but we may choose to add WhenMatched optimizations in the<br>
future to enhance sharing.<br>
<br>
There are various ways to construct the merge tactics. The most<br>
general are currently called<br>
<br>
traverseMaybeMissing :: Applicative f => (k -> x -> f (Maybe y)) -><br>
WhenMissing f k x y<br>
<br>
and<br>
<br>
zipWithMaybeAMatched :: Applicative f => (k -> x -> y -> f (Maybe z))<br>
-> WhenMatched f k x y z<br>
<br>
These directly convert functions into merge tactics with the<br>
corresponding types. There are various others that are simpler and/or<br>
faster. Generally, the pure tactics have the advantage of being able<br>
to process a whole subtree in a single Applicative action. Non-Maybe<br>
tactics have the advantage of skipping balance checks. dropMissing and<br>
preserveMissing are especially important because they can skip over<br>
whole subtrees, either pruning them or leaving them alone.<br>
<br>
Pure:<br>
<br>
dropMissing :: Applicative f => WhenMissing f k x y<br>
-- dropMissing :: SimpleWhenMissing k x y<br>
<br>
preserveMissing :: Applicative f => WhenMissing f k x x<br>
-- preserveMissing :: SimpleWhenMissing k x x<br>
<br>
mapMaybeMissing :: Applicative f => (k -> x -> Maybe y) -> WhenMissing f k x y<br>
-- mapMaybeMissing :: (k -> x -> Maybe y) -> SimpleWhenMissing k x y<br>
<br>
mapMissing :: Applicative f => (k -> x -> y) -> WhenMissing f k x y<br>
-- mapMissing :: (k -> x -> y) -> SimpleWhenMissing k x y<br>
<br>
filterMissing :: Applicative f => (k -> x -> Bool) -> WhenMissing f k x x<br>
-- filterMissing :: (k -> x -> Bool) -> SimpleWhenMissing k x x<br>
<br>
<br>
zipWithMaybeMatched :: Applicative f => (k -> x -> y -> Maybe z) -><br>
WhenMatched f k x y z<br>
-- zipWithMaybeMatched :: (k -> x -> y -> Maybe z) -> SimpleWhenMatched k x y z<br>
<br>
zipWithMatched :: Applicative f => (k -> x -> y -> z) -> WhenMatched f k x y z<br>
-- zipWithMatched :: (k -> x -> y -> z) -> SimpleWhenMatched k x y z<br>
<br>
Applicative:<br>
<br>
traverseMissing :: Applicative f => (k -> x -> f y) -> WhenMissing f k x y<br>
<br>
filterAMissing :: Applicative f => (k -> x -> f Bool) -> WhenMissing f k x x<br>
<br>
zipWithAMatched :: Applicative f => (k -> x -> y -> f z) -><br>
WhenMatched f k x y z<br>
<br>
<br>
Thanks,<br>
David Feuer<br>
_______________________________________________<br>
Libraries mailing list<br>
<a href="mailto:Libraries@haskell.org" target="_blank">Libraries@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries</a><br>
</blockquote></div></div>