haskell连接多级monad
我正在学习haskell,并尝试尽可能多地使用应用函子而不是monad。这是非常整洁和容易组成。然而,偶尔一些类型,如IO[IO[a]]或IO-Maybe-IO-Maybe-a会在代码中出现,这给我带来了很大的麻烦。显然,单子在这些场景中是不可避免的 我知道对于单级monad有一个简单的操作,比如join::m a->m a。对于多级单子有什么相似的吗?monad变形金刚里有什么吗 非常感谢 如果您注意到mn_u是一个monad转换器,那么您可以定义此操作。当我们注意到IO可能a与MaybeT IO a相同时,情况就是这样:然后我们只使用MaybeT的连接。我们可以在很大程度上做到这一点,因为事实上,可能和IO层特别好 另一方面,并非所有合成的单子都是单子变压器。特别是,IO[a]并不是真正的一个。我们希望有和加入看起来像haskell连接多级monad,haskell,monads,applicative,Haskell,Monads,Applicative,我正在学习haskell,并尝试尽可能多地使用应用函子而不是monad。这是非常整洁和容易组成。然而,偶尔一些类型,如IO[IO[a]]或IO-Maybe-IO-Maybe-a会在代码中出现,这给我带来了很大的麻烦。显然,单子在这些场景中是不可避免的 我知道对于单级monad有一个简单的操作,比如join::m a->m a。对于多级单子有什么相似的吗?monad变形金刚里有什么吗 非常感谢 如果您注意到mn_u是一个monad转换器,那么您可以定义此操作。当我们注意到IO可能a与MaybeT
newtype ListT m a = ListT { runListT :: m (Maybe (a, ListT m a)) }
在这种情况下,我们可以将IO[a]转换为ListIO a,反之亦然,但这些操作并不是相互颠倒的。的确,IO[a]本身不是单子,不能加入。单子一般不通勤,但您可以提供您需要的所有特定情况:
{-# 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
编辑
您可以通过提供可遍历的实例来避免定义新的typeclass。数据中有Maybe和[]的实例。可遍历:
简单的回答是,newtype ListT m a=ListT m[a]遵循其他类型的模式,这些类型成功地成为单子,但没有遵循一般m的规则本身。我有点不精确,例如列表m是单子,正好在[]和m通勤时,例如m[a]与[ma]同构。这是。还有一个问题,从更具操作性的角度来看,问题在于m[a]与我在回答中给出的列表不同,我将所有m效应整合到顶部。这意味着list effects和m-effects分批运行,而不是交织运行,这通常是满足monad transformer定律所必需的。如果您查看,您将看到流函数允许我们从ListT m a转换为m[a]。值得注意的是,此操作演示了如何将所有m效应一起粉碎的问题。正如我所指出的,它也会导致堆叠,因为这意味着你不能真正地将列表延迟地流式处理,所有的m效果都会同时被强制执行。我不确定,在今天早上的光线下,为什么我叫它stream,实际上我觉得它类似于zipList的情况,但不能证明/反驳monad定律。对于IO[IO[a]],目前我使用fmap序列将它们转换为IO[a]],然后执行join和fmap join。它将成为IO[a],但我认为它相当丑陋。这种机制完全分离IO和[]效应。只要您感兴趣的特定效果具有IO[a]~[IO a],例如效果通勤,它就可以工作。某些IO效果可能会出现这种情况,但您会注意到,您对结果列表的使用和现实世界中出现的效果将变得不同步。这在惰性IO的情况下最明显是糟糕的,它要么被IO上的连接和序列强制严格,要么由于资源利用率(例如文件句柄)不与列表使用相互转换而不确定。非常感谢!但我觉得这更像是另一个特殊的变压器实现。。。
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)