我正在从“学习Haskell的伟大成就”教程中学习Haskell,现在进入writer monads。这是我不知道的例子。
var fs = require('fs');
function find(name, cb){
fs.readFile('./db.json', 'utf8', function(err, data){
if(err) return cb(err)
data = JSON.parse(data);
cb(null, data[name]);
});
}
find('student', function(err, data){
console.log(data);
})
我正在尝试了解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) -- shouldn't return (3*5) result in (15,[]) ?
ghci> runWriter $ multWithLog
(15,["Got number: 3","Got number: 5"]) -- how did that happen?
块返回的w
单子变量中的单调值Writer w a
是如何改变的。本教程未详细介绍如何进行do
。
mappend
的类型声明和Writer
作为monad的实例声明由本教程给出
Writer
如果根据实例声明,newtype Writer w a = Writer { runWriter :: (a, w) }
instance (Monoid w) => Monad (Writer w) where
return x = Writer (x, mempty)
(Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
的结果为return x
,而对等式Writer (x, mempty)
的{{1}}为mempty
,则不应该[a]
,哪个等于[]
,哪个等于return (a*b)
?
return (3*5)
我将上面的命令交给了ghci,它返回了(15,[])
类型的值,该元组包含了一个空列表。
ghci> return (15) :: Writer [String] Int
WriterT (Identity (15,[]))
我改用绑定运算符重写了WriterT
块。上面的代码给出的结果与教程中的原始代码相同。
我的印象是multWithLog :: Writer [String] Int
multWithLog = logNumber 3 >>= (\a ->
logNumber 5 >>= (\b ->
return (a*b)))
仅从do
的结果中提取>>=
Int
并将其交给3
,然后{ logNumber 3
作用于其他值((\a -> logNumber 5 ...etc.)
),依此类推。这些操作如何导致Writer monad的logNumber
部分被更改?
答案 0 :(得分:2)
根据您发布的代码
(Writer (x,v)) >>= f =
let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
我们可以看到实际上仅通过f
参数调用了x
。
因此,在logNumber 3 >>= \a -> ...
中,变量a
确实绑定到3
。
但是,>>=
在调用f
之后会执行某些操作,即它将v
与v'
组合在一起。在您的示例中,v
是来自[String]
的{{1}}的{{1}}。取而代之的是logNumber 3
和["Got number: 3"]
一起对v'
求值,就是\a -> ...
。
a=3
是["Got number: 5"]
,它将列表连接在一起。因此,我们得到了最终结果。
允许我有点草率,而忽略mappend
包装器。我们得到
++
直觉上,我们可以假设作家monad中的值是有效的计算,它返回一个值(如Writer
),并且副作用是在字符串列表中附加了一些消息。所有这些消息的日志在monad内部都是不可见的(我们只能追加到日志中),并且只有在使用return (a*b)
= (a*b, [])
logNumber 5 >>= \b -> return (a*b)
= logNumber 5 >>= \b -> (a*b, [])
= (5, ["Got number: 5"]) >>= \b -> (a*b, [])
= (a*5, ["Got number: 5"] `mappend` [])
= (a*5, ["Got number: 5"])
logNumber 3 >>= \a -> logNumber 5 >>= \b -> return (a*b)
= logNumber 3 >>= \a -> (a*5, ["Got number: 5"])
= (3, ["Got number: 3"]) >>= \a -> (a*5, ["Got number: 5"])
= (3*5, ["Got number: 3"] `mappend` ["Got number: 5"])
= (15, ["Got number: 3", "Got number: 5"])
从monadic上下文中退出时,才在最后可用。 >
答案 1 :(得分:0)
这应该解释一下:
> runWriter (return 15) :: (Int, [String])
(15,[]) -- == runWriter $ writer (15, mempty)
> runWriter (logNumber 3)
(3,["Got number: 3"]) -- == runWriter $ writer (3, ["Got number: 3"])
> runWriter (logNumber 5)
(5,["Got number: 5"]) -- == runWriter $ writer (5, ["Got number: 5"])
> runWriter (logNumber 3 >> logNumber 5)
(5,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"]
> runWriter (logNumber 3 >> logNumber 5 >> return 15 )
(15,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"] ++ []
> runWriter (logNumber 3 >>= (\_ -> logNumber 5 >>= (\_ -> return 15 ) ) )
(15,["Got number: 3","Got number: 5"])
> runWriter (logNumber 3 >>= (\i -> logNumber 5 >>= (\j -> return (i*j) ) ) )
(15,["Got number: 3","Got number: 5"])
最后一行的单子表达式等同于multWithLog
的{{1}}块。
注意lambda函数的嵌套:lambda函数
do
位于laemda函数
内 (\j -> return (i*j) )
这就是为什么 (\i -> logNumber 5 >>= (\j -> return (i*j) ) )
中的i
引用 outer lambda函数的参数return (i*j)
的原因,该参数是从最外面的单子动作表达式{{ 1}}。
如何?因为根据您在问题中引用的i
的定义,所以我们有
logNumber 3
即
>>=
每个 runWriter ( Writer (x,v) >>= f )
=
runWriter ( let (Writer (y, u)) = f x in Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in runWriter ( Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in (y, v `mappend` u)
动作中的“单值”不“变”。每个动作将其“单态值”贡献到 runWriter ( logNumber 5 >>= (\j -> return j) )
= -------- f -----
runWriter ( writer (5, ["Got number: 5"]) >>= (\j -> writer (j, mempty)) )
= -- x ------- v ------- -------- f ---------------
let Writer (y, u) = ( (\j -> writer (j, mempty)) 5 )
-------- f --------------- x
in (y, ["Got number: 5"] `mappend` u)
= ------- v -------
let (y, u) = (5, mempty) in (y, ["Got number: 5"] `mappend` u)
=
(5, ["Got number: 5"] `mappend` mempty)
块的Writer
型组合计算的整体“单值”中,该计算由其Writer
构建do
进行的类型子计算,由{{1中的 语义 元组的1}}字段代表操作(Writer
的 实现 )。
同样,通过组合每个元组的单等值部分(即它们的mappending
字段)来组合此总值。单面组合为Writer
,它是通过snd
类型的计算在后台为我们完成的。
对于列表,Writer
,而对于列表snd
。
您的问题,然后:
mappend
等于Writer
吗?
它应该而且确实如此,就像我们在答案开头看到的那样。
mappend [a] [b] == [a] ++ [b] == [a,b]
与mappend [a,b] [] == [a,b] ++ [] == [a,b]
包装器
没关系。两者都是同构的,因为return (a*b)
是无操作的。 (15,[])
是Writer monad的monad-transformers实现的一部分;本书中给出的内容更简单,更易于理解。
这些操作如何导致Writer单子的
Writer
部分被更改?
未由特定作家使用的特定Monoid的WriterT
进行更改,但进行了组合;作为 monadic组合的一部分,即monadic绑定的Identity
定义;作为Monads的泛化函数调用协议,而Writer Monad的泛化是在幕后收集Monoid值,因此,除了用户函数在露天进行工作外,还可以将其附加在阴影中:
WriterT
拥抱[String]
-符号,是您的朋友。它可以帮助我们抽象。