无法理解 State Monad 如何在此代码中获取它的状态

Unable to understand how State Monad get it's state in this code

我有点理解 State Monad 在顺序执行中传播新值的用处。但是在下面的代码中,我无法理解 addResult 每次评估时如何以及在何处获取更新状态。

fizzBuzz :: Integer -> String
fizzBuzz n
   | n `mod` 15 == 0 = "FizzBuzz"
   | n `mod` 5 == 0 = "Buzz"
   | n `mod` 3 == 0 = "Fizz"
   | otherwise = show n

fizzbuzzList :: [Integer] -> [String]
fizzbuzzList list = execState (mapM_ addResult list) []

addResult :: Integer -> State [String] ()
addResult n = do
    xs <- get
    let result = fizzBuzz n
    put (result : xs)

main :: IO ()
main = mapM_ putStrLn $ fizzbuzzList [1..100]

此代码计算生成

1, 2, Fizz...

我只是想不通 addResult 产生的新值是如何附加到先前产生的列表中的。你能帮我理解一下 mapM_ addResult list 是怎么回事吗?

State 可以被认为是像

这样定义的
data State s a = s -> (a, s)

这意味着

Integer -> State [String] ()

等同于

Integer -> [String] -> ((), [String])

所以addResult接受一个整数,returns一个函数接受一个状态,returns一个包含新状态的元组。

mapM_,粗略地说,将一组这些函数链接在一起。使用常规映射会产生一个 State 函数列表,每个函数都需要一个状态并返回一个新状态。 mapM_ 进一步将每个 State 绑定到前一个。最终结果不是 State 值的列表,而是形成管道的单个 State 值。

execState 然后在管道的一端提供初始状态,returns 从另一端提供最终状态。

正如您正确观察到的那样,State monad 用于通过一系列计算将一些外部 'state' 值串联起来。但是,您询问在对列表 list 的每个值多次调用您的函数 addResult :: Integer -> State [String] () 时,这种状态是如何 'persisted' 的。诀窍是 mapM_ 的定义。我们将从考虑更简单的函数 mapM:

开始
mapM f [] = return []
mapM f (x:xs) = do
    fx <- f x
    fxs <- mapM f xs
    return (fx : fxs)

如果我们在脑海中 'expand' 这个递归定义,例如,一个示例列表 [x1,x2,x3,x4,...,xn],我们将观察到 mapM f [x1,...,xn] 的值将是另一个单子计算:

do
    fx1 <- f x1
    fx2 <- f x2
    -- etc.
    fxn <- f xn
    return [fx1,fx2,...,fxn]

所以 mapM 基本上 'sticks together' 一堆单子计算变成一个大的,通过 运行 按顺序将它们组合在一起。这解释了如何构建一个列表而不是生成许多较小的列表:addResult 开头的 get 从最后一个 运行[=34= 获取状态 ],而不是从一开始,因为您要 运行 将所有计算结合在一起,如下所示:

do
    fl0 <- addResult (list !! 0)
    fl1 <- addResult (list !! 1)
    -- etc. like before

(如果你仔细阅读,你会发现我一直在谈论 mapM,但你实际上使用了 mapM_。它们完全相同,只是后者 returns ().)