3

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

这是一段代码:

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函数上相乘。

我不明白为什么这些值是3and 5。不应该是包含在Monada中的值吗?在这种情况下元组?bWriter

例如, with this is with the MaybeMonad,a并且b将是3and 5

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

在这种情况下,这对我来说是有意义的,因为ab接收里面的内容Just。但与最初的例子相比,ab收到了部分价值。

4

3 回答 3

5

很容易看出 a = 3 和 b = 5,因为它们都在返回函数上相乘。我不明白为什么这些值是 3 和 5。 a 和 b 不应该是包含在 Writer Monad 中的值吗?在这种情况下是元组?

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

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

于 2015-11-23T23:06:49.977 回答
3

在这种情况下,这对我来说是有意义的,因为ab接收里面的内容Just。但与最初的例子相比,ab收到了部分价值。

这种描述并没有什么问题,但我们也可以换一种方式看待这种情况。在...

  a <- Just 3

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

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

... 非常相似:a只接收3值。可以说,[String]注释留在后台,通过单子绑定相互结合mappend,很像combine s 以便with give和with anything give 。 (>>=)MaybeJustJustJustNothingNothing

于 2015-11-23T22:43:59.213 回答
2

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

实际上,这样的单子并不“包含”任何东西。它实际上不是太空服或墨西哥卷饼,你知道...

在 的情况下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

可以给出atype 的值T。(在您的示例中,MisWriter [String]Tis Int,所以a只能有 type Int。)

这是否真的发生、发生的频率、值的来源以及它的含义取决于特定的 monad。尽管如此,单子定律可以告诉你很多关于整个计算的行为。但是在学习 monad 时,最好忘记这一切,简单地看许多不同的例子并自己涉足 monad,在某些时候你会对它们有直觉。不要试图通过类比来“理解”单子

于 2015-11-23T22:36:47.777 回答