理解 Writer Monad 的例子

Understanding example on Writer Monad

我正在 Learn You A 一书中了解 Writer Monad Haskell。

这是一段代码:

import Control.Monad.Writer

logNumber :: Int -> Writer [String] Int
logNumber num = writer (num, ["Got number: " ++ show num])

multWithLog :: Writer [String] Int
multWithLog = do
  a <- logNumber 3
  b <- logNumber 5
  return (a * b)

当运行multWithLog时,结果是这样的:

*Main> runWriter multWithLog
(15,["Got number: 3","Got number: 5"])

这一行:

a <- logNumber 3
b <- logNumber 5

很容易看出 a = 3b = 5,因为它们都在 return 函数上相乘。

我不明白的是为什么这些值是 35ab 不应该是 Writer Monad 中包含的值吗?在这种情况下元组?

例如,对于 Maybe Monad,ab 将是 35:

do
  a <- Just 3
  b <- Just 5
  return (a * b)

在这种情况下,这对我来说很有意义,因为 ab 接收 Just 中的内容。但是对于初始示例,ab 仅接收部分值。

“值包含在 monad 中”的概念有点模糊。有点效果,但不严格。

真的,这样的 monad 不“包含”任何东西。这是not actually a spacesuit or burrito,你知道...

Writer的情况下,你可以明确地说这是一个类型,其值包含日志片段和计算结果。后者是您可以解释为 monad 的“内容”,并且是您可以使用 a <- 检索的内容。但总的来说,根本不需要有这样的内容。

事实上,继续使用您的 Maybe 示例:

do a <- Just 3
   Nothing
   b <- Just 5
   return (a * b)

在这种情况下,Nothing“中断”了计算,因此 Just 5 永远无法将值 5 注入 a

要记住的是,如果 action :: M T 对于某些 monad M,则

do
   ...
   a <- action

可以给a一个T类型的值。 (在你的例子中,MWriter [String]TInt,所以 a 只能有类型 Int。)

这是否真的发生、发生的频率、值的来源以及它的含义取决于特定的 monad。尽管如此,monad laws can tell you a lot about the whole computation will behave. But while learning about monads, it's probably best to forget about all this, simply look at many different examples and dabble with monads yourself, at some point you'll get an intuition for them. Don't try too hard to “understand” monads through analogies.

In this case it makes sense to me, since a and b receive the content inside Just. But with the initial example, a and b only receive part of the value.

这个描述其实没有什么不妥,但是我们也可以换个角度来看这个情况。在...

  a <- Just 3

...我们也可以说 a 只收到 Just 3 的一部分——Just 包装器没有做到。从这个角度来看,发生了什么...

  a <- writer (3, ["Got number: " ++ show 3])

... 非常相似:a 仅接收 3 值。 [String] 注释,可以说,留在后台,通过 monadic 绑定使 mappend 相互融合,很像 (>>=) 组合 Maybe 以便 JustJust 一起给出 JustNothing 与任何东西一起给出 Nothing

It is easy to see that a = 3 and b = 5, since both of them are being multiplied on the return function. What I don't understand is why those values are 3 and 5. Shouldn't a and b be the values that contain inside the Writer Monad? In this case tuples?

没有。我认为回答这个问题最简单的方法就是实现 Writer 类型并研究它的 Monad class 实例:

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

instance Functor (Writer w) where
  fmap f (Writer (a, w)) = Writer (f a, w)

instance Monoid w => Applicative (Writer w) where
  pure a = Writer (a, mempty)
  Writer (f, w) <*> Writer (a, w') = Writer (f a, w <> w')

instance Monoid w => Monad (Writer w) where
  return = pure
  Writer (a, w) >>= f = let (b, w') = runWriter (f a)
                        in Writer (b, w <> w')

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

正如您在 >>= 的实例方法中看到的那样,函数 f 应用于 a 值,而不是整个元组。语法 a <- logNumber 3 使用 >>= 脱糖,因此绑定到 a 的值将是 Writer 环绕的元组的第一个元素。