haskell加入多级monad

时间:2014-11-22 09:45:23

标签: haskell monads applicative

我学习haskell,尽量使用applicative functor而不是monad。它非常整洁,易于构图。但是,有时像代码中会出现IO [IO [a]]IO Maybe IO Maybe a等类型,这会给我带来很大的麻烦。显然monad在这些场景中变得不可避免。

我知道单级monad有join:: m (m a) -> m a这样的平面操作。多级monad有什么类似的东西吗? monad变形金刚有什么东西吗?

非常感谢!

2 个答案:

答案 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