【问题标题】:Understanding example on Writer Monad理解 Writer Monad 的例子
【发布时间】:2016-02-26 04:41:40
【问题描述】:

我正在学习《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 函数上相乘。

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

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

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

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

【问题讨论】:

  • 当您在Writer monad 中执行x &lt;- e 时,x 通常不会捕获编写器状态。您可以将 logNumber 重写为 logNumber num = tell "..." &gt;&gt; return num - 这是一个产生与给出的相同数字的计算,但作为副作用“记录”一个字符串。当您“绑定”时,您正在访问“返回”的值 - 这只是数字。您可以使用listen(x,loggedString) &lt;- listen $ logNumber 3 获得所需的行为。

标签: haskell monads writer-monad


【解决方案1】:

很容易看出 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)

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

【讨论】:

  • 如果它对任何人有帮助,我也添加了 execWriter 的定义:execWriter :: Writer w a -&gt; w execWriter m = snd (runWriter m)
【解决方案2】:

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

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

  a <- Just 3

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

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

... 非常相似:a 仅接收 3 值。 [String] 注释,也就是说,留在后台,通过单子绑定将 mappend 相互连接,就像 (&gt;&gt;=) 结合 Maybes 以便 JustJust 得到 @987654339 @ 和 Nothing 与任何东西都会给出 Nothing

【讨论】:

    【解决方案3】:

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

    真的,这样的单子并不“包含”任何东西。是not actually a spacesuit or burrito,你懂的...

    Writer 的情况下,你可以清楚地说这是一个type,它的值包含log-sn-ps 和computation-results。后者是您可能解释为 monad 的“内容”,并且是您可以使用 a &lt;- 检索的内容。但总的来说,根本不需要任何这样的内容。

    事实上,以你的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 可以告诉你很多关于整个计算的行为。但是在学习 monad 时,最好忘掉这一切,简单地看许多不同的例子并自己涉足 monad,在某些时候你会对它们有直觉。 Don't try too hard to “understand” monads through analogies.

    【讨论】:

      猜你喜欢
      • 2020-09-22
      • 1970-01-01
      • 2014-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-06
      • 1970-01-01
      相关资源
      最近更新 更多