可能的 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