我尝试编写类似于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
答案 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