我正在尝试理解Haskell中的monad,所以我正在阅读https://wiki.haskell.org/All_About_Monads
让我们考虑一下上面的一段代码:
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 :(得分:4)
不要考虑如何将“monadic函数”重复应用于monadic值,而是考虑如何将monadic函数列表“折叠”为单个函数,以后可以将其应用于monadic值。 / p>
在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
我们越来越近了。我们需要像(.)
这样的合成运算符,但对于monadic函数,它可以组合两个monadic函数并生成另一个函数。某种类型Monad m => (a -> m a) -> (a -> m a) -> a -> m a
。 Looking for the signature in Hoogle,我们发现稍微更通用(但仍然合适)的运算符(<=<)
:
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
只留下一个细节:monadic功能组合的“身份”是什么?嗯,它是return
,它将纯粹的价值置于“中性”的一元背景中。对于Maybe
,return
只是Just
构造函数。
所以,总之:如果你想组合一个monadic函数列表,你可以这样做:
combineMonadicFunctions :: Monad m => [a -> m a] -> a -> m a
combineMonadicFunctions fs = foldr (<=<) return fs
现在,您可以使用(>>=)
将结果应用于原始monadic值。
答案 1 :(得分:3)
您可以使用iterate
执行此操作iterate (>>= father) (return s) !! 10
将10更改为父亲所需的申请数量。
答案 2 :(得分:2)
如果您有关于monadic操作的列表(例如,[IO x]
或[Maybe Int]
或某些内容),则可以使用sequence
函数将所有这些操作链接在一起。 (请注意,它们必须具有相同的类型才能将它们放在列表中。)
如果你有一个输入列表,并且想要将它们传递给一个monadic函数(例如,String -> Maybe Int
),你可以在列表中map
你的函数,从而得到一个monadic动作列表。然后,您可以使用sequence
来链接这些链接。但这是一种常见的模式,因此有一个功能:您可以直接使用mapM
。
一般情况下,请在Control.Monad
中查看其他有用的与monad相关的功能。