可能的 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 bg 的类型为 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 法则。

  1. 左身份法 - return x >>= f = f x
return z >>= f 
= (Just z) >>= f 
= f z
  1. 正确的身份法 - m >>= return = m
  • Just 数据构造函数
Just z >>= return
= return z
= Just z  
  • Nothing 数据构造函数
Nothing >>= return
= Nothing 
  1. 结社法 - (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