2
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)

So suppose for this code one runs:

 madd (Counter 10 43) (Counter 32 1)

and one gets Counter 42 46.

I do not get how this produces this result. So madd "calls" monad Counter, then passes + function to >>= part of the monad instance. But then I find how the monad instance calls/passes functions/results very puzzling.

Can anyone explain in detail how intermediate calculations work?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
NCL
  • 29
  • 3
  • 1
    Have you tried writing `madd` in terms of `>>=`, desugaring the `do`? You should see that the function passed to `>>=` is more complex than `+`. – chi Jun 25 '17 at 20:38
  • 4
    I'm not sure I'd recommend studying this Monad example, since it breaks the law that `(x >>= return) == x`. – amalloy Jun 25 '17 at 21:21
  • 1
    @amalloy I agree. It seems easy to fix, though: just drop the `+1` in the definition of `(>>=)`. Seems the interesting content of the question would be basically unchanged from this fix, too. – Daniel Wagner Jun 25 '17 at 22:48

2 Answers2

1

tl;dr : Embrace the do, eschew the bind ! It's an implementational detail anyway. do could really be treated as an axiomatic, primary notation, being as "monads" are a fancy way to say "programs" in the first place (insert a hedging qualifier here).


The do notation is desugared into the bind -based code,

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

Conversely, going in the reverse direction,

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

Thus your Monad instance's bind definition,

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

can be informally re-written as

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

Now we can manipulate your do code directly, to see what's going on:

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

that is, madd (Counter a i) (Counter b j) produces Counter (a+b) (i+j+2), 2 being the number of bind transformation applications.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • I downvoted this answer because of the "tl;dr" part. `>>=` is not an implementation detail! It('s signature) is the essential abstraction for the notion of sequencing effectful computations; if you sit down and think hard enough about what sequencing means you'll eventually come up with `>>=`. While one can be somewhat productive using `do` without thought to what it means, sooner or later you do have to learn about `>>=`. This question and your answer are evidence of that! – Benjamin Hodgson Jun 26 '17 at 10:47
  • @BenjaminHodgson thanks for dropping the note. I realized that it'll be a highly controversial remark. :) I'm just in search of something to make it more accessible and less intimidating for the newbies. It is after all true that `do { v <- m ; f v } === join $ do { v <- m ; return (f v) } === f =<< m` (and `do { v <- m ; return (f v) } === f <$> m`), so one can pick a notation that's more to their liking; the types of course stay the same. – Will Ness Jun 26 '17 at 16:40
  • @BenjaminHodgson so, some like it *bind*, some like *programmable semicolon* better; and some even prefer *MonadComprehensions*! Any one of the three could be the starting point... – Will Ness Jun 26 '17 at 17:08
  • You have to start somewhere, of course. I was just put off by the implication that one shouldn't think about bind at all. – Benjamin Hodgson Jun 26 '17 at 18:45
0

When desugared your do expression in madd becomes

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

So you get 43 plus 1 from first and second counter plus two, each for each bind in madd

When learning monads it is good idea to avoid do notation and always remember specific definitions of bind (>>=) for monad we are analyzing

Antisthenes
  • 420
  • 2
  • 8