haskell - LYAH - 在链接 Writer monad 时理解关于“tell”的评论
问题描述
问题在底部以粗体显示。
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 中是否发生了一些变化,这使得上述引用的陈述不清楚/不准确?
解决方案
等效的重写是进一步
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 () ))))
确实会有 type Writer [String] ()
,所以如果要指定签名Writer [String] Int
,它确实不会编译。
没有类型签名问题,“日志”即收集的[String]
列表对于两种变体都是相同的,因为return
不会改变收集的输出“日志”。
推荐阅读
- base64 - Angular 8 无法从 base 64 字符串下载 docx 文件
- php - 如何使用 Symfony Form 组件渲染基于网格的表单?
- javascript - 如何在 Rails 表单中添加颜色选择器
- jenkins - 如何在 Jenkins 管道中获取项目根目录?
- css - 调整 div 的高度,使其内容填充 100% 宽度
- css - Bootstrap 4 更改 col 的宽度
- hreflang - 避免重复hreflang标签
- reactjs - 为什么react-hook-form输入中的第一个字母死了
- ggplot2 - 在ggplot2中将数字标签添加到geom_hline
- python - 如何从