可能的 monad
Maybe
用于表示可能为空的值 - 类似于其他语言中的 null
。通常它被用作可能以某种方式失败的函数的输出类型。
考虑以下功能:
halve::Int -> Maybe Int
halve x
| even x = Just (x `div` 2)
| odd x = Nothing
将 halve
视为一个动作,取决于 Int
,它试图将整数减半,如果它是奇数则失败。
我们怎样 halve
整数三次?
takeOneEighth::Int -> Maybe Int -- (after you read the 'do' sub-section:)
takeOneEighth x =
case halve x of -- do {
Nothing -> Nothing
Just oneHalf -> -- oneHalf <- halve x
case halve oneHalf of
Nothing -> Nothing
Just oneQuarter -> -- oneQuarter <- halve oneHalf
case halve oneQuarter of
Nothing -> Nothing -- oneEighth <- halve oneQuarter
Just oneEighth ->
Just oneEighth -- return oneEighth }
takeOneEighth
是链接在一起的三个halve
步骤的序列。- 如果一个
halve
步骤失败,我们希望整个组合takeOneEighth
失败。 - 如果
halve
步骤成功,我们希望将结果向前推进。
instance Monad Maybe where
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= f = Nothing -- infixl 1 >>=
Just x >>= f = Just (f x) -- also, f =<< m = m >>= f
-- return::a -> Maybe a
return x = Just x
现在我们可以写:
takeOneEighth::Int -> Maybe Int
takeOneEighth x = halve x >>= halve >>= halve -- or,
-- return x >>= halve >>= halve >>= halve -- which is parsed as
-- (((return x) >>= halve) >>= halve) >>= halve -- which can also be written as
-- (halve =<<) . (halve =<<) . (halve =<<) $ return x -- or, equivalently, as
-- halve <=< halve <=< halve $ x
Kleisli 组成 <=<
定义为 (g <=< f) x = g =<< f x
,或等同于 (f >=> g) x = f x >>= g
。有了它,上面的定义变得公正
takeOneEighth::Int -> Maybe Int
takeOneEighth = halve <=< halve <=< halve -- infixr 1 <=<
-- or, equivalently,
-- halve >=> halve >=> halve -- infixr 1 >=>
每个 monad 都应该遵守三个 monad 法则,即每个类型都是 Monad
类型类的一个实例:
1. return x >>= f = f x
2. m >>= return = m
3. (m >>= g) >>= h = m >>= (\y -> g y >>= h)
其中 m
是 monad,f
的类型为 a -> m b
,g
的类型为 b -> m c
。
或者等效地,使用上面定义的 >=>
Kleisli 合成运算符:
1. return >=> g = g -- do { y <- return x ; g y } == g x
2. f >=> return = f -- do { y <- f x ; return y } == f x
3. (f >=> g) >=> h = f >=> (g >=> h) -- do { z <- do { y <- f x; g y } ; h z }
-- == do { y <- f x ; do { z <- g y; h z } }
遵守这些定律使得更容易推理 monad,因为它保证使用 monadic 函数并组合它们的行为方式与其他 monad 相似。
让我们来看看 Maybe
monad 是否遵守三个 monad 法则。
- 左身份法 -
return x >>= f = f x
return z >>= f
= (Just z) >>= f
= f z
- 正确的身份法 -
m >>= return = m
Just
数据构造函数
Just z >>= return
= return z
= Just z
Nothing
数据构造函数
Nothing >>= return
= Nothing
- 结社法 -
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
Just
数据构造函数
-- Left-hand side
((Just z) >>= f) >>= g
= f z >>= g
-- Right-hand side
(Just z) >>= (\x -> f x >>= g)
(\x -> f x >>= g) z
= f z >>= g
Nothing
数据构造函数
-- Left-hand side
(Nothing >>= f) >>= g
= Nothing >>= g
= Nothing
-- Right-hand side
Nothing >>= (\x -> f x >>= g)
= Nothing