2016-04-07 22 views
2

我想了解Haskell的單子,所以我讀https://wiki.haskell.org/All_About_Monads向左展開粘結劑在Haskell

讓我們考慮了一段代碼從上面的網站:

maternalGrandfather :: Sheep -> Maybe Sheep 
maternalGrandfather s = (return s) >>= mother >>= father 

fathersMaternalGrandmother :: Sheep -> Maybe Sheep 
fathersMaternalGrandmother s = (return s) >>= father >>= mother >>= mother 

而且一切都清楚了。但我想知道如何做一個很長的(也許是無限的)序列。我的意思是比如:

expand :: Int -> Sheep -> MaybeSheep 

和例如擴大expand 10 s使得(return s) >>= father >>= father >>= father >>= father >>= father .. (10 times)

如何實現它。也許使用遞歸來擴展,但我無法想象停止時可以返回什麼。

+0

一個*無限序列的monadic動作將永遠不會返回。 '(>> =)'運算符對此太嚴格了。 – MathematicalOrchid

+1

@MathematicalOrchid這通常不是真的,'>> ='的操作語義完全取決於函數是如何爲任何特定的monad定義的。例如'fix(\ x - > x:[[1]] >> = scanl(+)1)'是完全明確的。 – user2407038

回答

4

我們不應該考慮如何將「monadic函數」重複應用於一元值,而是應該考慮如何將單值函數列表「摺疊」爲單個函數列表,以便稍後將其應用於一元值。

在Haskell,原型組合子認爲 「坍塌」 列表被稱爲foldr

foldr :: (a -> b -> b) -> b -> [a] -> b 

foldr接收功能,一個初始值列表作爲參數。它的作用是用函數替換列表的每個構造函數:,並用列表中的空白構造函數[]替換爲初始值。例如,請考慮以下內嵌列表:

4 : 5 : 77 : 34 : [] 

假設我們要將列表中的所有元素添加到88。我們可以做這樣的

foldr (+) 88 (4 : 5 : 77 : 34 : []) 

這實在是等於

4 + 5 + 77 + 34 + 88. 

好了,現在想象列表的元素是a -> a類型的功能。我們可以將兩個函數與組合運算符(.)結合使用,但是,我們應該用什麼函數替換列表[]的末尾?我們不希望添加任何進一步的修改,所以我們把「中性元素」的成分,標識功能id

foldr (.) id ((\x -> x + 1) : (\y -> y + 2) : []) 

這等於

(\x -> x + 1) . (\y -> y + 2) . id 

我們越來越近了。我們需要一個組合運算符,如(.),但是對於一元函數,它可以組合兩個一元函數並生成另一個函數。某種類型的Monad m => (a -> m a) -> (a -> m a) -> a -> m aLooking for the signature in Hoogle,我們發現稍微一般(但仍適用)運營商(<=<)

(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c 

這裏只有一個細節留給:什麼是「身份」爲一元函數組合?那麼,這是return,這是一個純粹的價值在一個「中立」monadic上下文。在Maybe的情況下,return只是Just的構造函數。

所以,結論是:如果你想結合的一元函數的列表,你可以做這樣的:

combineMonadicFunctions :: Monad m => [a -> m a] -> a -> m a 
    combineMonadicFunctions fs = foldr (<=<) return fs 

現在你可以將結果應用到使用(>>=)原來的一元價值。

3

爲此,您可以使用iterator

iterate (>>= father) (return s) !! 10 

變化10至父親的期望應用的數量。

+0

好吧,它不是一般的。我有一個列表,我的停止約束是[] – Gilgamesz

+0

@Gilgamesz - 你總是可以使用'find'來添加停止約束。 – jamshidh

2

如果您有單點擊動作列表(例如,[IO x][Maybe Int]或其他),則可以使用sequence函數將所有這些動作鏈接在一起。 (請注意,它們都必須具有相同的類型才能將它們放在列表中。)

如果您有一個輸入列表,並且您想將它們傳遞給一元函數(例如,String -> Maybe Int),那麼您可以將map函數放在列表中,從而生成一列monadic操作。然後您可以使用sequence來鏈接這些。但這是一種常見的模式,所以有一個功能:您可以直接使用mapM作爲這種情況。

一般來說,請在Control.Monad左右,以查看其他有用的monad相關函數。