Haskell:< - 用monad表示法

时间:2014-05-28 06:19:57

标签: haskell monads

我在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)中的两个整数,否则我们不能做乘法。

我的误会是什么?有人可以帮忙吗?非常感谢。

4 个答案:

答案 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)

您的第一个声明ab是元组,并不完全正确。

如果我们有类似

的话
foo = do
  ...
  baz <- bar
  quux

如果bar :: m a,那么我们可以将baz视为aquux类型的值,因为<-已经(大致)去了

foo = do
  ...
  bar >>= \baz -> quux

答案 3 :(得分:0)

Zeta explained很好地为什么abInt而不是对。

我认为混淆的主要原因是最后一行

return (a*b) 

如果a = 3b = 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"] ++ [])