我学习haskell,尽量使用applicative functor而不是monad。它非常整洁,易于构图。但是,有时像代码中会出现IO [IO [a]]
或IO Maybe IO Maybe a
等类型,这会给我带来很大的麻烦。显然monad在这些场景中变得不可避免。
我知道单级monad有join:: m (m a) -> m a
这样的平面操作。多级monad有什么类似的东西吗? monad变形金刚有什么东西吗?
非常感谢!
答案 0 :(得分:3)
如果您注意到m (n _)
是monad变换器,那么您可以定义此操作。当我们注意到IO (Maybe a)
与MaybeT IO a
相同时,情况确实如此:我们只需使用MaybeT
' s join
。我们可以在很大程度上做到这一点,因为Maybe和IO" layer"特别好。
另一方面,并非所有"组成" monad是monad变形金刚。特别是,IO [a]
并不是真的。 The true ListT transformer我们希望join
看起来像
newtype ListT m a = ListT { runListT :: m (Maybe (a, ListT m a)) }
我们可以将IO [a]
转换为ListT IO a
,反之亦然,但不这些操作相互颠倒的情况。确实,IO [a]
本身并不是一个单子,不能join
。
答案 1 :(得分:1)
Monads一般不会通勤,但您可以提供所需的所有特定情况:
{-# LANGUAGE MultiParamTypeClasses #-}
import Control.Monad
class (Monad m, Monad n) => Swappable m n where
swap :: m (n a) -> n (m a)
instance Swappable [] IO where
swap = sequence
instance Swappable Maybe IO where
swap Nothing = return Nothing
swap (Just mx) = fmap return mx
cut :: Swappable m n => m (n (m a)) -> n (m a)
cut = liftM join . swap
squash :: Swappable m n => n (m (n (m a))) -> n (m a)
squash = (>>= cut)
一个例子:
x :: IO [IO [()]]
x = return $ map (\s -> putStrLn s >> return [()]) ["ab","c"]
y :: IO (Maybe (IO (Maybe ())))
y = return $ Just $ putStrLn "z" >> return (Just ())
main = squash x >> squash y
打印
ab
c
z
修改强>
您可以通过提供Traversable
的实例来避免定义新的类型类(Maybe
中有[{1}}和[]
的实例):
Data.Traversable