【问题标题】:How binding works in function monads in Haskell?Haskell 的函数单子中的绑定是如何工作的?
【发布时间】:2019-02-17 12:06:18
【问题描述】:

从学你一个haskell:http://learnyouahaskell.com/for-a-few-monads-more

函数的 Monad 实例是这样的:

instance Monad ((->) r) where  
    return x = \_ -> x  
    h >>= f = \w -> f (h w) w 

我无法理解以下输出:

import Control.Monad.Instances  

addStuff :: Int -> Int  
addStuff = do  
    a <- (*2)  
    b <- (+10)  
    return (a+b)

addStuff 3 返回 19。书上说 3 作为参数传递给 (*2)and (+10)。怎么样?

h &gt;&gt;= f = \w -&gt; f (h w) w 看来,(h w) 似乎被绑定到 a 或 b。 那么,为什么 6 没有被传入(+10)?

这里我对f的理解是当(*2)h时,f就是addStuff的最后两行。当(+10)h 时,faddStuff 的最后一行(在本例中为return 语句)。

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    让我们先desugar the do block [Haskell'10 report]

    addStuff = do
        a <- (*2)
        b <- (+10)
        return (a+b)

    相当于:

    addStuff = (*2) >>= \a -> ((+10) >>= \b -> return (a + b))
    

    内部绑定表达式 ((+10) &gt;&gt;= \b -&gt; return (a + b)),因此可以转换,绑定定义为:

    \w -> (\b -> return (a + b)) ((+10) w) w
    

    如果我们用const替换return,我们得到:

    \w -> (const . (a+)) ((+10) w) w
    

    因此,我们有一个函数将w 作为输入,然后在(w+10)w 上调用const . (a+),因此它将忽略最后一个w。语义上等价于:

    (a+) . (+10)
    

    所以现在我们的addStuf 相当于:

    addStuff = (*2) >>= \a -> ((a+) . (+10))
    

    如果我们现在再次使用绑定运算符的定义,我们会看到:

    \w -> (\a -> ((a+) . (+10))) ((*2) w) w
    

    或更短:

    \w -> (\a -> ((a+) . (+10))) (w*2) w
    

    我们现在可以用(w*2) 替换a 并获得:

    \w -> ((w*2)+) . (+10)) w
    

    所以我们的addStuf 相当于:

    addStuff w = (w*2) + (w+10)
    

    或更简单:

    addStuff w =  3*w + 10
    

    【讨论】:

      【解决方案2】:

      Haskell do 表示法是一系列 &gt;&gt;= 绑定的语法糖;在这种情况下,对于这样的事情:

      addStuff = (*2) >>= (\a -> (+10) >>= (\b -> return (a + b)))
      

      【讨论】:

        【解决方案3】:

        在计算步骤之间传递更新的参数(因此将充当状态的角色)是另一个 monad 的工作,即 State monad。

        函数又名 Reader monad 比这更简单,工作更少:

        -- (return x) w = x
        -- (h >>= f)  w = f (h w) w 
        
        (h >>= (\a ->   g >>= (\b ->   return (f a b))))   w                   
        =   
               (\a ->   g >>= (\b ->   return (f a b)))  (h w)   w
        = 
        (g >>= (\b ->   return (f  (h w)  b)))   w
        = 
               (\b ->   return (f  (h w)  b)))  (g w)   w
        = 
                        return (f  (h w)  (g w))        w
        = 
                                f  (h w)  (g w)  
        

        因此,输入参数w 被传递不变 到两个(通过扩展,所有)计算步骤。或者在您询问的特定情况下,

            liftM2 (+) ( *2) ( +10)  w 
         = 
                   (+) (w*2) (w+10) 
        

        liftM2 函数等效于您显示的do 块,

        liftM2 f h g =
           do {
             a <- h ;
             b <- g ;
             return (f a b) }
         =
           h >>= (\ a ->
            g >>= (\ b ->  return (f a b) ))
        

        在任何单子中。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-27
          • 1970-01-01
          • 1970-01-01
          • 2017-11-27
          • 2012-10-19
          • 1970-01-01
          • 1970-01-01
          • 2012-02-22
          相关资源
          最近更新 更多