2014-11-22 65 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