2014-11-22 56 views
2

我正在学习haskell,并试图尽可能多地使用applicative函子而不是monad。这是非常简洁的写作。然而,偶尔会有一些类型如IO [IO [a]]IO Maybe IO Maybe a会在代码中上升,这给我带来很大麻烦。显然monad在这些情况下成为不可避免的。haskell加入多级monad

我知道有一个平坦的操作,如单层单体的join:: m (m a) -> m a。多级单子有什么相似之处吗?任何在monad变压器?

非常感谢!

回答

3

如果你注意到m (n _)是一个monad变压器,那么你可以定义这个操作。当我们注意到IO (Maybe a)MaybeT IO a相同时,我们确实如此:然后我们只使用MaybeTjoin。由于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。

+0

简短的回答是,'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

+0

从一个更具操作性的POV中,问题是'm [a]'与我在回答中给出的'ListT'有所不同,它将所有'm'效应合并到顶端。这意味着列表效应和“m”效应可以批量运行,而不是交织在一起,这对于满足一般的单变量法则是必需的。 – 2014-11-22 11:19:01

+1

如果你看[我的要点详细说明'ListT'完成 - 正确的实现](https://gist.github.com/tel/8efa535a1d4a95646adc),你会看到'stream'函数允许我们从' ListT ma'到'm [a]'。值得注意的是,这个操作演示了“粉碎所有”一起效应“的问题。正如我所指出的那样,它也是“打击叠加”,因为它意味着你不能实际上懒散地排列清单 - 所有的“m”效果都会被迫立即强制执行。 (我不确定,在现在的早晨,为什么我实际上称它为'stream') – 2014-11-22 11:21:10

1

单子不一般的通勤,但您可以提供所有的特殊情况下,你需要:

{-# 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) 
+0

非常感谢!但我觉得它更像是另一个adhoc变压器的实施...... – ccaapton 2014-11-23 02:03:20

+0

@ccaapton,我编辑了答案。 – user3237465 2014-11-23 15:29:48