我在Writer部分看到了以下示例,了解了一个haskell。
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = Writer (x, ["Got number: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (a*b)
结果是:
ghci> runWriter multWithLog
(15,["Got number: 3","Got number: 5"])
我知道用符号&lt; - 会从上下文中提取值。所以 a 和 b 应该是格式(Int,[String])的元组,根据 Writer 的声明。我觉得 a 和 b 应该是 return(a * b)中的两个整数,否则我们不能做乘法。
我的误会是什么?有人可以帮忙吗?非常感谢。
答案 0 :(得分:2)
暂时忽略logNumber
的定义,只关心其签名:
logNumber :: Int -> Writer [String] Int
我们知道Writer s
是monad(给定Monoid s
)。让我们使用类型同义词StringWriter
一秒钟让事情变得更清楚:
type StringWriter = Writer [String]
logNumber :: Int -> StringWriter Int
请记住,Writer s
是一个monad,而StringWriter
只是该monad的类型同义词。我们还更改了multWithLog
的类型:
multWithLog :: StringWriter Int
现在应该很清楚StringWriter
上下文中包含的值是Int
,而不是一对。现在,回到你的陈述:
我知道在符号
<-
中将从上下文中提取值。
此时,显而易见的是,提取的值的类型为Int
。
答案 1 :(得分:1)
monad中的主要“工作”是在&gt;&gt; = (绑定)函数中生成的。 Monads是某种计算构建器。因此,如果您想知道具体Monad中的计算是如何发生的,您需要打开它的&gt;&gt; = 和返回 funcs的实现。
E.g。作家的monad implementation:
instance (Monoid w) => Monad (Writer w) where
return a = Writer (a, mempty)
m >>= k = Writer $ let
(a, w) = runWriter m
(b, w') = runWriter (k a)
in (b, w `mappend` w')
所以需要另一位作家 m ,得到它的计算结果 a (在你的情况下为Int)和附加上下文 w ([String] )通过执行runWriter。然后它将结果输入到函数 k ,返回另一个Writer。获得结果后,它会通过应用 mappend 与上下文结合使用,如果列表是 ++ 。
因此,字符串的组成出现在 w mappend
w'中。所有这些都发生在幕后。
如您所见,&gt;&gt; = 只为结果 a (Int)提供功能 k ,没有任何上下文( [串])。这就是为什么你的代码中没有任何对。
也许如果你在没有“做”语法糖的情况下重写你的代码,它会更清楚:
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = Writer (x, ["Got number: " ++ show x])
multWithLog =
logNumber 3 >>=
\a -> logNumber 5 >>=
\b -> return (a*b)
答案 2 :(得分:0)
您的第一个声明a
和b
是元组,并不完全正确。
如果我们有类似
的话foo = do
...
baz <- bar
quux
如果bar :: m a
,那么我们可以将baz
视为a
中quux
类型的值,因为<-
已经(大致)去了
foo = do
...
bar >>= \baz -> quux
答案 3 :(得分:0)
Zeta explained很好地为什么a
和b
是Int
而不是对。
我认为混淆的主要原因是最后一行
return (a*b)
如果a = 3
和b = 5
,这不应该导致Writer (15, [])
?嗯,确实如此,["Got number: 3","Got number: 5"]
从何而来呢?
答案在于>>=
的实施。让我们删除do
符号的语法糖:
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (a*b)
相当于
logNumber 3 >>= (\a ->
logNumber 5 >>= (\b ->
return (a*b)))
相当于
Writer (3, ["Got number: 3"]) >>= (\a ->
Writer (5, ["Got number: 5"]) >>= (\b ->
Writer (a*b, [])))
相当于
Writer (3, ["Got number: 3"]) >>= (\a ->
Writer (5, ["Got number: 5"]) >>= (\b ->
Writer (3*5, [])))
请记住,>>=
是一个二元函数(带有两个参数的函数),它返回Writer (Int, [String])
,即Writer
Int
和一个日志。函数>>=
返回的日志是第一个参数的日志["Got number 3", "Got number 5"]
和第二个参数的日志[]
的组合。
我撒了o_o
Sidenote :如果你看一下括号,那么日志追加的实际顺序将是
["Got number: 3"] ++ (["Got number: 5"] ++ [])