Hey all,<br><br>Amid the discussion of inconsistencies between Data.IntMap and Data.Map, I thought I&#39;d throw in some more ideas.  I&#39;ve been working on a rather thorough generalized trie map implementation, which has done nothing if not force me to make really very generalized signatures for every method offered by Data.Map, etc.  Some of these generalizations are, I think, independently useful, and I wanted to throw them out for discussion before creating a ticket.<br>

<br>A function that I call extract/extractWith/extractWithKey:<br><br>extractWithKey :: Alternative f =&gt; (k -&gt; a -&gt; f (z, Maybe a)) -&gt; Map k a -&gt; f (z, Map k a)<br><br>which is actually defined as follows:<br>

<br>&gt; extractWithKey f (Bin n kx x l r) = fmap (\ (z, l&#39;) -&gt; (z, Bin n kx x l&#39; r)) (extractWithKey f l) &lt;|&gt;<br>&gt;      fmap (\ (z, x&#39;) -&gt; (z, maybe (glue l r) (\ xx -&gt; bin kx xx l r) x&#39;)) (f kx x) &lt;|&gt; fmap (\ (z, r&#39;) -&gt; (z, Bin n kx x l r&#39;)) (extractWithKey f r)<br>

&gt; extractWithKey f Nil = empty<br><div style="margin-left: 40px;"><br></div>If m is fromList [(k1, x1), (k2, x2), ..., (kn, xn)], then <br><br>&gt; fst &lt;$&gt; extractWithKey (\ k a -&gt; pure (f k a, &lt;whatever&gt;)) m == f k1 x1 &lt;|&gt; f k2 x2 &lt;|&gt; ... &lt;|&gt; f kn xn<br>

<br>and then the second component is obtained by modifying a single element accordingly and taking the alternative over every element to choose from.<br><br>A few observations:  with an appropriate Alternative instance for Data.Monoid.First and Data.Monoid.Last,<br>

&gt; extractWithKey (\ k a -&gt; return ((k, a), Nothing)) m == First (minViewWithKey m)<br><br>so this generalizes minViewWithKey and maxViewWithKey according to the First Alternative instance.  (Note that the extractWithKey implementation is also O(log n), because the natural First Alternative instance will ignore the irrelevant pathways lazily,<br>

<br>&gt; extractWithKey (\ k a -&gt; if p k a then pure ((k, a), Nothing) else empty)<br><br>in the First Alternative instance, will extract the first element of the map that satisfies p, or will return Nothing if there is no such element, and will take O(i) time to do so if the first satisfying element is at index i,<br>

<br>&gt; mapPermK :: Int -&gt; Map k a -&gt; [[(k, a)]]<br>&gt; mapPermK 0 m = [[]]<br>&gt; mapPermK (n+1) m = do    ((k1, a1), m&#39;) &lt;- extractWithKey (\ k a -&gt; return ((k, a), Nothing)) m<br>&gt;                                         liftM ((k1, a1) :) (mapPermK n m)<br>

<br>returns a list of all permutations of k distinct associations from the map m, and does it exactly as lazily as one might hope.  (Generating combinations efficiently is somewhat harder, and I&#39;m not sure this approach grants much in the way of additional efficiency beyond a couple other approaches I can think of.)<br>

<br>In any event, you get the idea: this is a preposterously general method, albeit difficult to describe.  It is moderately easier to describe the individual projections, which I name arbitrarily,<br><br>about :: Alternative f =&gt; (k -&gt; a -&gt; f z) -&gt; Map k a -&gt; f z<br>

modify :: Alternative f =&gt; (k -&gt; a -&gt; f (Maybe a)) -&gt; Map k a -&gt; f (Map k a)<br><br>Finally, I&#39;ll define neighborhood.  Internally, neighborhood has the signature<br><br>neighborhood :: Ord k =&gt; k -&gt; Map k a -&gt; (Last (k, a), Maybe a, First (k, a))<br>

<br>where neighborhood k m = (sup [(k&#39;, a) | k&#39; &lt; k], lookup k m, inf [(k&#39;, a) | k&#39; &gt; k]) == case splitLookup k m of (mL, v, mR) -&gt; (fmap fst (maxViewWithKey mL), v, fmap fst (minViewWithKey mR))<br>

<br>is defined as follows:<br><br>neighborhood k Tip = (empty, empty, empty)<br>neighborhood k (Bin _ kx x l r) = case compare k kx of<br><div style="margin-left: 40px;">LT -&gt; case neighborhood k l of<br><div style="margin-left: 40px;">

(lb, v, ub) -&gt; (lb, v, ub &lt;|&gt; pure (kx, x))<br></div>EQ -&gt; (Last (fmap fst (maxViewWithKey l)), Just x, First (fmap fst (minViewWithKey r)))<br><div style="margin-left: 40px;">-- we can also use the *about* generalization here, since we only need the minimum and maximum associations<br>

</div>GT -&gt; case neighborhood k r of<br><div style="margin-left: 40px;">(lb, v, ub) -&gt; (pure (kx, x) &lt;|&gt; lb, v, ub)<br></div></div><div style="margin-left: 40px;"><div style="margin-left: 40px;"><br></div></div>

Using MonadPlus or Alternative instances for First and Last are rather elegant here, and I strongly support adding their Applicative, Alternative, Monad, and MonadPlus instances to base.<br><br clear="all">Louis Wasserman<br>

<a href="mailto:wasserman.louis@gmail.com">wasserman.louis@gmail.com</a><br><a href="http://profiles.google.com/wasserman.louis">http://profiles.google.com/wasserman.louis</a><br>