我可以用Applicative而不是Monad重写这个类似unionWith的函数吗?

时间:2016-05-08 04:37:35

标签: haskell dictionary monads applicative

我尝试编写类似于here's my question about that的函数,但可能会失败。原来的人使用了Maybe,这确实是Monad,所以monadic对我来说效果很好。但是我想知道它是否可以用Applicative重写,因为我用纯粹的fmap来满足unionWith的类型要求。或者使用Data.Map中的其他函数而不是unionWith?

{-# LANGUAGE RankNTypes #-}
import Control.Monad
import Data.Map
unionWithM :: (Monad m, Traversable t)
          => (forall a. (a -> a -> a)
              -> t a
              -> t a
              -> t a
             )
          -> (v -> v -> m v)
          -> t v
          -> t v
          -> m (t v)
unionWithM u f a b = sequenceA (u f' (pure <$> a) (pure <$> b))
  where f' x y = join $ f <$> x <*> y

unionWithOriginal :: Ord k => (a -> a -> Maybe a) -> Map k a -> Map k a -> Maybe (Map k a)
unionWithOriginal f a b = sequenceA (unionWith f' (Just <$> a) (Just <$> b))
  where f' x y = join $ f <$> x <*> y

1 个答案:

答案 0 :(得分:7)

是的,你可以,但你需要一个中间数据结构。 问题是您在应用函数之前包装了Map的值,这就是f'类型为m a -> m a -> m a的原因。要将f转换为f',您需要join,即Monad。诀窍是在联合之后应用函数。 为此你可以使用(Maybe a, Maybe a)这有点乱,所以你可以使用These数据类型。如果我们手动推出它,你会得到

data These a b = That a | This b | These a b

unionWith' f a b = let theses = unionWith These (That <$> a) (This <$> b)
                   in sequenceA (f' <$> theses)
    where f' (That a) = pure a
          f' (This b) = pure b
          f' (These a b) = f a b

如果您使用these包,则可以将其简化为

 unionWith'' f a b = sequenceA $ alignWith (these pure pure f)  a b