问题在底部以粗体显示。
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)))
然后这本书提出了一个对我来说似乎不清楚的观点,如果不是不准确的话:It's important that
return (a*b)
is the last line, because the result of the last line in ado
expression is the result of the whole do expression. Had we puttell
as the last line,()
would have been the result of thisdo
expression. We'd lose the result of the multiplication. However, the log would be the same.
因此,我的第一个疑问来了:如果
tell
结果 ()
,那么代码不应该也不应该编译,因为 ()
无法匹配预期类型 Int
, 也不是 ()
以外的任何其他类型本身;那么作者想告诉我们什么?为了使这个非基于意见,自从本书编写以来,Haskell 中是否发生了一些变化,导致上述引用的陈述不清楚/不准确?
最佳答案
等效的重写是进一步
multWithLog = logNumber 3 >>= (\ x ->
logNumber 5 >>= (\ y ->
tell ["something"] >>= (\ () -> -- () NB
return (x*y) >>= (\ result ->
return result ))))
那就是()
那tell ["something"]
“回归”。显然,洗牌multWithLog2 = logNumber 3 >>= (\ x ->
logNumber 5 >>= (\ y ->
return (x*y) >>= (\ result ->
tell ["something"] >>= (\ () ->
return () ))))
确实会有类型 Writer [String] ()
, 所以如果签名指定 Writer [String] Int
,它确实不会编译。没有类型签名问题,即“日志”,即收集的
[String]
两个变体的列表将相同,如 return
不会更改收集的输出“日志”。
关于haskell - LYAH - 在链接 Writer monad 时理解关于 "tell"的评论,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62182040/