我正在学习haskell,并试图尽可能多地使用applicative函子而不是monad。这是非常简洁的写作。然而,偶尔会有一些类型如IO [IO [a]]
或IO Maybe IO Maybe a
会在代码中上升,这给我带来很大麻烦。显然monad在这些情况下成为不可避免的。haskell加入多级monad
我知道有一个平坦的操作,如单层单体的join:: m (m a) -> m a
。多级单子有什么相似之处吗?任何在monad变压器?
非常感谢!
我正在学习haskell,并试图尽可能多地使用applicative函子而不是monad。这是非常简洁的写作。然而,偶尔会有一些类型如IO [IO [a]]
或IO Maybe IO Maybe a
会在代码中上升,这给我带来很大麻烦。显然monad在这些情况下成为不可避免的。haskell加入多级monad
我知道有一个平坦的操作,如单层单体的join:: m (m a) -> m a
。多级单子有什么相似之处吗?任何在monad变压器?
非常感谢!
如果你注意到m (n _)
是一个monad变压器,那么你可以定义这个操作。当我们注意到IO (Maybe a)
与MaybeT IO a
相同时,我们确实如此:然后我们只使用MaybeT
的join
。由于Maybe和IO“层”特别好,我们可以做到这一点。
另一方面,并非所有“合成”单子都是单子变压器。特别是,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]
本身不是一个monad,不能是join
ed。
单子不一般的通勤,但您可以提供所有的特殊情况下,你需要:
{-# 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
和实例在Data.Traversable
[]
):
import Data.Traversable as T
import Control.Monad
cut :: (Traversable t, Monad t, Monad m) => t (m (t a)) -> m (t a)
cut = liftM join . T.sequence
squash :: (Traversable t, Monad t, Monad m) => m (t (m (t a))) -> m (t a)
squash = (>>= cut)
非常感谢!但我觉得它更像是另一个adhoc变压器的实施...... – ccaapton 2014-11-23 02:03:20
@ccaapton,我编辑了答案。 – user3237465 2014-11-23 15:29:48
简短的回答是,'NEWTYPE ListT毫安= ListT(M [A])'遵循成功monad的其他类型的模式,但不遵循规则本身的一般'm'。我当时有点不确切 - 例如''''''''''''''''''ListT m' * *是一个monad。 'm [a]'与'[m a]'同构。这是[在文档中指出](http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-List.html)。还有[Wiki上的这个页面](https://www.haskell.org/haskellwiki/ListT_done_right)。 – 2014-11-22 11:18:02
从一个更具操作性的POV中,问题是'm [a]'与我在回答中给出的'ListT'有所不同,它将所有'm'效应合并到顶端。这意味着列表效应和“m”效应可以批量运行,而不是交织在一起,这对于满足一般的单变量法则是必需的。 – 2014-11-22 11:19:01
如果你看[我的要点详细说明'ListT'完成 - 正确的实现](https://gist.github.com/tel/8efa535a1d4a95646adc),你会看到'stream'函数允许我们从' ListT ma'到'm [a]'。值得注意的是,这个操作演示了“粉碎所有”一起效应“的问题。正如我所指出的那样,它也是“打击叠加”,因为它意味着你不能实际上懒散地排列清单 - 所有的“m”效果都会被迫立即强制执行。 (我不确定,在现在的早晨,为什么我实际上称它为'stream') – 2014-11-22 11:21:10