Haskell Maybe 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(即作为Monadtypeclass的一个实例的每个类型)都应遵循三个monad法则:

1.  return x >>= f  =  f x
2.    m >>= return  =  m
3. (m >>= g) >>= h  =  m >>= (\y -> g y >>= h)

mmonad在哪里,f具有typea -> m b和gtype 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 } }

遵守这些定律使对单子的推理变得容易得多,因为它保证了使用单子函数并组成它们的行为与其他单子一样合理。

让我们检查一下Maybemonad是否遵守这三个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