Haskell monad 示例:解释它是如何工作的

The Haskell monad example: explanation of how it works

madd a b = do aa <- a
              bb <- b
              return (aa + bb)

data Counter a = Counter a Int
    deriving (Show,Eq)

instance Functor Counter where
  fmap f (Counter a i) = Counter (f a) i

instance Applicative Counter where
  pure x = Counter x 0
  Counter f i <*> Counter x j = Counter (f x) (i + j)

instance Monad Counter where
  return x = Counter x 0
  Counter a i >>= f = 
    let Counter b j = f a
     in Counter b (i + j + 1)

所以假设这个代码运行:

 madd (Counter 10 43) (Counter 32 1)

一个人得到 Counter 42 46.

我不明白这是如何产生这个结果的。所以 madd "calls" monad Counter,然后将 + 函数传递给 monad 实例的 >>= 部分。但是后来我发现 monad 实例 calls/passes functions/results 非常令人费解。

谁能详细解释中间计算的工作原理?

脱糖后,madd 中的 do 表达式变为

a >>= \aa -> b >>= \bb -> return (a + b)

所以你从第一个和第二个计数器得到 43 加 1 加 2,每个都对应 madd 中的每个绑定

学习 monad 时,最好避免 do 表示法,并始终记住我们正在分析的 monad 的绑定 (>>=) 的具体定义

tl;dr :拥抱 do,避开 bind !无论如何,这是一个实施细节。 do 确实可以被视为公理化的基本符号,因为 "monads" 首先是 的一种奇特方式(在此处插入对冲限定符)。


do 符号被脱糖到基于 bind 的代码中,

do { v <- m ; f v }  ===  m >>= f

反之,反之,

Counter a i >>= f  ===  do { v <- Counter a i ; f v }

因此您的 Monad 实例的 bind 定义,

Counter a i >>= f  =  Counter b (i + j + 1)  where
                      Counter b j = f a

可以非正式地重写为

instance Monad Counter where
  return <b>x</b>      =  Counter <b>x</b> 0
  do { v <- Counter <b>x</b> <i>i</i>
     ; f v }    =  Counter y (<i>i</i> + j + 1)  where        -- the <em>bind</em> transformation
                   Counter y j = f v
                   v           = <strong>x</strong></pre>

现在我们可以直接操作您的 do 代码,看看发生了什么:

madd (Counter <b>10</b> <em>43</em>) (Counter <b>32</b> <em>1</em>)
= do { aa <- Counter <b>10</b> <em>43</em>
     ; bb <- Counter <b>32</b> <em>1</em>
     ; return (aa + bb)
     }
= do { aa <- Counter <b>10</b> <em>43</em>
     ; do { bb <- Counter <b>32</b> <em>1</em>                         -- by Monad Laws
          ; return (aa + bb)
          }}
= do { aa <- Counter <b>10</b> <em>43</em>
     ; f aa } where f aa = do { bb <- Counter <b>32</b> <em>1</em>     -- abstraction
                              ; return (aa + bb)
                              }
= Counter y (<em>43</em> + j + 1)                               -- the <em>bind</em> transformation
  where Counter y j = f <b>10</b>
                    = do { bb <- Counter <b>32</b> <em>1</em>
                         ; return (<b>10</b> + bb)
                         }
                    = do { bb <- Counter <b>32</b> <em>1</em>
                         ; (return . (<b>10</b> +)) bb
                         }
                    = Counter y (<em>1</em> + j + 1)            -- the <em>bind</em> transformation
                      where Counter y j = (return . (<b>10</b> +)) <b>32</b>
                                        =  return 42
                                        = Counter 42 0
                    = Counter 42 (<em>1</em> + 0 + 1)
                    = Counter 42 2
= Counter 42 (<em>43</em> + 2 + 1)
= Counter 42 46 </pre>

也就是说,madd (Counter a i) (Counter b j)产生Counter (a+b) (i+j+2)2bind转换应用的数量。