Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using monadic functions with Data.Map (fx unionWith)

I would like to union two Map instances with a monadic function. This becomes a problem because of the unionWith type signature:

unionWith :: Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a

I'm looking for a smart way to do this. Here is my naive implementation:

monadicUnionWith :: (Monad m, Ord k) => (a -> a -> m a) -> Map k a -> Map k a -> m (Map k a)
monadicUnionWith f mapA mapB = do
  let overlapping = toList $ intersectionWith (\a b -> (a,b)) mapA mapB
  mergedOverlapping <- liftM fromList $ mapM helper overlapping
  return $ union (union mergedOverlapping mapA) mapB
  where
    helper (k, (a,b)) = do
      c <- f a b
      return (k, c)

Note that union is left biased

like image 613
RasmusWL Avatar asked Nov 10 '13 22:11

RasmusWL


1 Answers

Not sure if it is more efficient, but it is somewhat cooler (as it involves storing monadic values in the map):

monadicUnionWith :: (Monad m, Ord k) => (a -> a -> m a) -> Map k a -> Map k a -> m (Map k a)
monadicUnionWith f mapA mapB =
  Data.Traversable.sequence $ unionWith (\a b -> do {x <- a; y <- b; f x y}) (map return mapA) (map return mapB)

And if you want you can use

(\a b -> join (liftM2 f a b))

as the parameter to unionWith, or even

((join.).(liftM2 f))
like image 194
Joachim Breitner Avatar answered Nov 05 '22 03:11

Joachim Breitner