Monad #

一、Monad概念 #

1.1 什么是Monad #

Monad是处理上下文计算的抽象:

haskell
class Applicative f => Monad f where
    return :: a -> f a
    (>>=)  :: f a -> (a -> f b) -> f b
    (>>)   :: f a -> f b -> f b

1.2 与Applicative对比 #

haskell
-- Applicative:函数应用
(<*>) :: f (a -> b) -> f a -> f b

-- Monad:可以基于前一个结果选择下一个操作
(>>=) :: f a -> (a -> f b) -> f b

1.3 基本示例 #

haskell
-- Maybe是Monad
instance Monad Maybe where
    return = Just
    Nothing >>= _ = Nothing
    Just x  >>= f = f x

-- 使用
Just 5 >>= \x -> Just (x * 2)  -- Just 10
Nothing >>= \x -> Just (x * 2) -- Nothing

二、常见Monad实例 #

2.1 Maybe #

haskell
instance Monad Maybe where
    return = Just
    Nothing >>= _ = Nothing
    Just x  >>= f = f x

-- 使用:链式安全操作
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)

-- 链式计算
calc :: Int -> Int -> Int -> Maybe Int
calc x y z = 
    safeDiv x y >>= \result ->
    safeDiv result z

-- 使用do语法
calc' :: Int -> Int -> Int -> Maybe Int
calc' x y z = do
    result <- safeDiv x y
    safeDiv result z

-- 示例
calc 100 5 2  -- Just 10
calc 100 0 2  -- Nothing

2.2 列表 #

haskell
instance Monad [] where
    return x = [x]
    xs >>= f = concat (map f xs)

-- 使用:列表推导
pairs :: [Int] -> [Int] -> [(Int, Int)]
pairs xs ys = do
    x <- xs
    y <- ys
    return (x, y)

-- 等价于
pairs' xs ys = [(x, y) | x <- xs, y <- ys]

-- 示例
pairs [1, 2] [3, 4]  -- [(1, 3), (1, 4), (2, 3), (2, 4)]

2.3 IO #

haskell
instance Monad IO where
    return = pure
    (>>=) = bindIO

-- 使用
main :: IO ()
main = do
    putStrLn "Enter name:"
    name <- getLine
    putStrLn ("Hello, " ++ name)

2.4 Either #

haskell
instance Monad (Either e) where
    return = Right
    Left e  >>= _ = Left e
    Right x >>= f = f x

-- 使用:错误处理链
validate :: Int -> Either String Int
validate x
    | x < 0     = Left "Negative"
    | x > 100   = Left "Too large"
    | otherwise = Right x

process :: Int -> Either String String
process x = do
    a <- validate x
    b <- validate (a * 2)
    return (show b)

-- 示例
process 30  -- Right "60"
process (-1)  -- Left "Negative"

2.5 函数(Reader) #

haskell
instance Monad ((->) r) where
    return = const
    f >>= g = \x -> g (f x) x

-- 使用
addAndMultiply :: Int -> Int
addAndMultiply = do
    x <- (+1)
    y <- (*2)
    return (x + y)

-- 等价于
addAndMultiply' = \r -> let x = r + 1
                             y = r * 2
                         in x + y

三、Monad定律 #

3.1 左单位元 #

haskell
-- return x >>= f == f x

-- 验证
return 5 >>= (\x -> Just (x * 2))  -- Just 10
(\x -> Just (x * 2)) 5  -- Just 10

3.2 右单位元 #

haskell
-- m >>= return == m

-- 验证
Just 5 >>= return  -- Just 5
[1, 2, 3] >>= return  -- [1, 2, 3]

3.3 结合律 #

haskell
-- (m >>= f) >>= g == m >>= (\x -> f x >>= g)

-- 验证
(Just 5 >>= (\x -> Just (x * 2))) >>= (\y -> Just (y + 1))
-- Just 11
Just 5 >>= (\x -> (\x -> Just (x * 2)) x >>= (\y -> Just (y + 1)))
-- Just 11

四、do语法 #

4.1 基本形式 #

haskell
-- do语法是Monad的语法糖
do
    x <- action1
    y <- action2
    action3 x y

-- 等价于
action1 >>= \x ->
action2 >>= \y ->
action3 x y

4.2 简化形式 #

haskell
-- 无绑定的操作
do
    action1
    action2

-- 等价于
action1 >> action2

4.3 let绑定 #

haskell
do
    x <- action
    let y = x * 2
    return y

-- 等价于
action >>= \x ->
let y = x * 2
in return y

五、实用函数 #

5.1 sequence #

haskell
import Control.Monad

-- sequence:将Monad列表转为列表的Monad
sequence :: Monad m => [m a] -> m [a]

-- 使用
sequence [Just 1, Just 2, Just 3]  -- Just [1, 2, 3]
sequence [Just 1, Nothing, Just 3] -- Nothing
sequence [putStrLn "a", putStrLn "b"]  -- 打印 a b,返回 [(), ()]

5.2 mapM和forM #

haskell
import Control.Monad

-- mapM:映射后序列
mapM :: Monad m => (a -> m b) -> [a] -> m [b]

-- forM:mapM的参数翻转
forM :: Monad m => [a] -> (a -> m b) -> m [b]

-- 使用
mapM print [1, 2, 3]  -- 打印 1 2 3,返回 [(), (), ()]

forM [1, 2, 3] $ \x -> do
    putStrLn $ "Processing " ++ show x
    return (x * 2)
-- 打印处理信息,返回 [2, 4, 6]

5.3 filterM #

haskell
import Control.Monad

-- filterM:Monad版本的filter
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]

-- 使用
filterM (\x -> return (x > 2)) [1, 2, 3, 4, 5]  -- [3, 4, 5]

-- IO版本
filterM doesFileExist ["a.txt", "b.txt", "c.txt"]
-- 返回存在的文件列表

5.4 foldM #

haskell
import Control.Monad

-- foldM:Monad版本的fold
foldM :: Monad m => (b -> a -> m b) -> b -> [a] -> m b

-- 使用
safeSum :: [Int] -> Maybe Int
safeSum = foldM (\acc x -> 
    if x < 0 
        then Nothing 
        else Just (acc + x)) 0

-- 示例
safeSum [1, 2, 3]  -- Just 6
safeSum [1, -1, 3] -- Nothing

5.5 when和unless #

haskell
import Control.Monad

-- when:条件为True时执行
when :: Monad m => Bool -> m () -> m ()

-- unless:条件为False时执行
unless :: Monad m => Bool -> m () -> m ()

-- 使用
main :: IO ()
main = do
    when True $ putStrLn "Yes"
    unless False $ putStrLn "Not false"

5.6 join #

haskell
import Control.Monad

-- join:展平嵌套Monad
join :: Monad m => m (m a) -> m a

-- 使用
join (Just (Just 5))  -- Just 5
join (Just Nothing)   -- Nothing
join [[1, 2], [3, 4]] -- [1, 2, 3, 4]

六、实践示例 #

6.1 安全计算链 #

haskell
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)

complexCalc :: Int -> Int -> Int -> Maybe Int
complexCalc a b c = do
    x <- safeDiv a b
    y <- safeDiv x c
    z <- safeDiv y 2
    return (z + 1)

-- 使用
complexCalc 100 5 2  -- Just 6
complexCalc 100 0 2  -- Nothing

6.2 状态处理 #

haskell
-- 简单的状态Monad
newtype State s a = State { runState :: s -> (a, s) }

instance Functor (State s) where
    fmap f (State g) = State $ \s ->
        let (x, s') = g s
        in (f x, s')

instance Applicative (State s) where
    pure x = State $ \s -> (x, s)
    State f <*> State g = State $ \s ->
        let (h, s') = f s
            (x, s'') = g s'
        in (h x, s'')

instance Monad (State s) where
    return = pure
    State f >>= g = State $ \s ->
        let (x, s') = f s
        in runState (g x) s'

-- 使用
type Counter = State Int

increment :: Counter ()
increment = State $ \s -> ((), s + 1)

getCount :: Counter Int
getCount = State $ \s -> (s, s)

runCounter :: Counter a -> (a, Int)
runCounter = flip runState 0

-- 示例
example :: Counter Int
example = do
    increment
    increment
    increment
    getCount

-- runCounter example = (3, 3)

6.3 日志记录 #

haskell
-- Writer Monad
newtype Writer w a = Writer { runWriter :: (a, w) }

instance Monoid w => Functor (Writer w) where
    fmap f (Writer (x, w)) = Writer (f x, w)

instance Monoid w => Applicative (Writer w) where
    pure x = Writer (x, mempty)
    Writer (f, w1) <*> Writer (x, w2) = Writer (f x, w1 <> w2)

instance Monoid w => Monad (Writer w) where
    return = pure
    Writer (x, w1) >>= f = 
        let (y, w2) = runWriter (f x)
        in Writer (y, w1 <> w2)

tell :: Monoid w => w -> Writer w ()
tell w = Writer ((), w)

-- 使用
type Log = Writer [String]

process :: Int -> Log Int
process x = do
    tell ["Starting with " ++ show x]
    let result = x * 2
    tell ["Result is " ++ show result]
    return result

-- runWriter (process 5) = (10, ["Starting with 5", "Result is 10"])

七、总结 #

Monad要点:

  1. 定义return>>=
  2. 常见实例:Maybe、[]、IO、Either、函数
  3. Monad定律:左单位元、右单位元、结合律
  4. do语法:Monad操作的语法糖
  5. 实用函数sequencemapMfilterMfoldM
  6. 应用:错误处理、状态管理、日志记录

掌握Monad后,让我们继续学习Monoid。

最后更新:2026-03-27