【发布时间】:2020-09-22 16:40:12
【问题描述】:
问题在底部以粗体显示。
LYAH 给出了使用 do 符号和 Writer monad 的示例
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = writer (x, ["number " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (x*y)
可以在没有do 符号的情况下重写定义:
multWithLog = logNumber 3 >>= (\x ->
logNumber 5 >>= (\y ->
return (x*y)))
到目前为止一切顺利。
之后,本书介绍了tell,并编辑了multWithLog的定义如下:
multWithLog = do
a <- logNumber 3
b <- logNumber 5
tell ["something"]
return (x*y)
又可以改写为:
multWithLog = logNumber 3 >>= (\x ->
logNumber 5 >>= (\y ->
tell ["something"] >>
return (x*y)))
然后这本书提出了一个对我来说似乎不清楚,如果不是不准确的话:
return (a*b)是最后一行很重要,因为do表达式中最后一行的结果是整个 do 表达式的结果。如果我们将tell作为最后一行,则()将是这个do表达式的结果。我们会丢失乘法的结果。但是,日志将是相同的。
因此,我的第一个疑问来了:如果tell 结果为(),那么代码不应该甚至不能编译,因为() 不能匹配预期的类型Int,也不匹配任何其他类型除了() 本身;那么作者想告诉我们什么?为了使这个不基于意见,自从本书编写以来,Haskell 发生了一些变化,导致上述引用的陈述不清楚/不准确?
【问题讨论】:
-
如果你删除
return (x*y)行,那么这确实不会编译,因为那时()将是结果。我真的看不出你引用的段落有什么问题? -
一方面它以日志将是相同的结尾。哪个日志,如果代码甚至没有编译?
-
好吧,作者可能假设您将签名更改为
Writer [String] ():)。在这种情况下,结果将为(),但仍会将["something"]写入日志。 -
这正是作者的重点。最后一行很重要,否则返回值(以及此处的类型)是不同的。
-
如果有人读到这篇文章想玩这个 LYAH 例子,看看它是否还能编译,试试 LYAH 的 Jupyter 改编版(第 13 章)github.com/jamesdbrock/learn-you-a-haskell-notebook
标签: haskell monads do-notation rewriting writer-monad