4

This code is from this article

I've been able to follow it until this part.

module Test where

type State = Int

data ST a = S (State -> (a, State))

apply        :: ST a -> State -> (a,State)
apply (S f) x = f x

fresh =  S (\n -> (n, n+1))

instance Monad ST where
    -- return :: a -> ST a
    return x   = S (\s -> (x,s))

    -- (>>=)  :: ST a -> (a -> ST b) -> ST b
    st >>= f   = S (\s -> let (x,s') = apply st s in apply (f x) s')

data Tree a = Leaf a | Node (Tree a) (Tree a) deriving (Show)


mlabel  :: Tree a -> ST (Tree (a,Int))
-- THIS IS THE PART I DON'T UNDERSTAND:
mlabel (Leaf x) = do n <- fresh
                     return (Leaf (x,n))
mlabel (Node l r) =  do l' <- mlabel l
                        r' <- mlabel r
                        return (Node l' r')

label t = fst (apply (mlabel t) 0)

tree = Node (Node (Leaf 'a') (Leaf 'b')) (Leaf 'c')

And label tree produces:

Node (Node (Leaf ('a',0)) (Leaf ('b',1))) (Leaf ('c',2))

I can see that >>= operator is the tool to 'chain' functions that return monads (or something like that).

And while I think I understand this code, I don't understand how this particular code works.

Specifically do n <- fresh. We haven't passed any argument to fresh, right? What does n <- fresh produces in that case? Absolutely don't understand that. Maybe it has something to do with currying?

duplode
  • 33,731
  • 7
  • 79
  • 150
user1685095
  • 5,787
  • 9
  • 51
  • 100

3 Answers3

8

Specifically do n <- fresh. We haven't passed any argument to fresh, right?

Exactly. We are writing for an argument that will be passed to fresh when we, for instance, do something like apply (mlabel someTree) 5. A nice exercise that will help you to see more clearly what is going on is first writing mlabel with explicit (>>=) instead of do-notation, and then replacing (>>=) and return with what the Monad instance says that they are.

duplode
  • 33,731
  • 7
  • 79
  • 150
5

The key thing to realise is that do notation gets translated into Monad functions, so

do n <- fresh
   return (Leaf (x,n))

is short for

fresh >>= (\n -> 
           return (Leaf (x,n))  )

and

do l' <- mlabel l
   r' <- mlabel r
   return (Node l' r')

is short for

mlabel l >>= (\l' -> 
              mlabel r >>= (\r' ->
                            return (Node l' r') ))

This will hopefully allow you to continue figuring out the code's meaning, but for more help, you should read up on the do notation for Monads.

Cactus
  • 27,075
  • 9
  • 69
  • 149
AndrewC
  • 32,300
  • 7
  • 79
  • 115
  • I could take apart what `fresh`, `mlabel` and `return` are doing here, but I think you'll learn more about the ST monad by figuring that out yourself. (i.e. I don't want to prematurely over-explain.) – AndrewC Jul 19 '15 at 23:43
  • 'mlabel l' gives us state transformer that takes State as input and produces (Leaf (x, oldState), NewState) right? If so, then how can we use bind for 'mlabel r' after that? Shouldn't function that we bind after ''mlabel l' have type Leaf (Char, State) -> ST b? – user1685095 Jul 20 '15 at 07:07
  • 1
    @user1685095 because ST is an instance of Monad: in `(>>=) :: Monad m => m a -> (a -> m b) -> m b`, `m` can be replaced with `ST`. So `ST a` can go on the left side of `>=`. And that is the type of `mlabel r` too. – Will Ness Jul 20 '15 at 07:50
  • 1
    @user1685095 `Leaf (Char, State) -> ST b` is not a type, because `Leaf` is a *data* constructor. `mlabel (l :: Tree a) :: ST (Tree (a,Int)) ~~ State -> ( Tree (a,Int), State)`. `l'` has type `Tree (a,Int)` but `l` has type `Tree a`, as does `r`. Notice that the second bind is *nested* inside the first bind's target function. The value in `l'` is put aside, and the second bind is performed, with the second target function, *inside the scope* where `l'` is accessible still, so `return` can use both `r'` and `l'`. – Will Ness Jul 20 '15 at 08:11
4

With the monadic "pipelining" inlined, your code becomes

fresh state = (state, state + 1)

mlabel (Leaf x) state =                   --  do
  let (n, state') = fresh state           --    n <- fresh
  in  (Leaf (x,n), state')                --    return (Leaf (x,n))

mlabel (Node l r) state =                 -- do
  let (l', state') = mlabel l state       --    l' <- mlabel l
  in let (r', state'') = mlabel r state'  --    r' <- mlabel r
     in  (Node l' r', state'')            --    return (Node l' r') 

main = let (result, state') = mlabel tree 0  
       in  print result                         

{- Or with arrows,

mlabel (Leaf x)   = Leaf . (x ,)  &&&  (+ 1)
mlabel (Node l r) = mlabel l >>> second (mlabel r)
                              >>> (\(a,(b,c)) -> (Node a b,c))
main              = mlabel tree >>> fst >>> print  $ 0
-}

Or in an imperative pseudocode:

def state = unassigned

def fresh ():
    tmp = state 
    state := state + 1     -- `fresh` alters the global var `state`
    return tmp             -- each time it is called

def mlabel (Leaf x):       -- a language with pattern matching
    n = fresh ()           -- global `state` is altered!
    return (Leaf (x,n))  

def mlabel (Node l r):
    l' = mlabel l          -- affects the global
    r' = mlabel r          --    assignable variable
    return (Node l' r')    --    `state`

def main:
    state := 0             -- set to 0 before the calculation!
    result = mlabel tree
    print result

Calculating the result changes the state assignable; it corresponds to the snd field in Haskell's (a, State) tuple. And the fst field of the tuple is the newly constructed tree, carrying a numbering alongside its data in its leaves.

These variants are functionally equivalent.

Perhaps you've heard the catch-phrase about monadic bind being a "programmable semicolon". Here the meaning of it is clear: it defines the "function call protocol" so to speak, that we use the first returned value as a calculated result, and the second returned value as the updated state, which we pass along to the next calculation, so it gets to see the updated state.

This is the state-passing style of programming (essential for e.g. Prolog), making the state change explicit but having to manually take care of passing along the correct, updated state. Monads allow us to abstract this "wiring" of state passing from one calculation to the next, so it is done automatically for us, at the price of having to think in imperative style, and having this state become hidden, implicit again (like the state change is implicit in the imperative programming, which we wanted to eschew in the first place when switching to the functional programming...).

So all that the State monad is doing is to maintain for us this hidden state, and passing it along updated between the consecutive calculations. So it's nothing major after all.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • Is my understading correct that mlabel l >>= (\l' -> mlabel r >>= (\r' -> return (Node l' r') )) gives curried functions? And this code could've been written as you've written it? mlabel l, state >>= \l' ... etc? – user1685095 Jul 23 '15 at 18:06
  • I understand what monads do essentially. I think I'm just not very comfortable with reading code that is written with curried functions. Functional code that you've wrote is basically the same code without currying, right? – user1685095 Jul 23 '15 at 18:10
  • `mlabel l >>= f == S (\s -> let (x,s') = apply (mlabel l) s in apply (f x) s')`. so it's a function of one argument `s`. I don't know what you mean by "curried" there. It's a function. In Haskell `g x y = ...` is the same as `g x = \y -> ...`. Both are *definitions*. These *equations* do not "create" new objects in memory as they are encountered - the whole program is compiled. And because of lazyness, we can analyze/transform the program by equational reasoning. And compilers are allowed to do so, to. – Will Ness Jul 23 '15 at 21:10
  • there's no `mlabel l, state >>= \l'`; rather, the function under `S` gets applied to a state, `apply (S f) s = f s`, so since `mlabel l >>= f = S \s-> ...`, there will be eventual application of `\s-> ...` to a state argument. Haskell's functions are curried, `f x y = ...` meaning that `f x` is a function, which gets applied ***perhaps later*** to `y`. So the function defined by `mlabel l` will be applied to some initial state, at some point. In your program, `label t = ...` does that. – Will Ness Jul 23 '15 at 21:10
  • you really should try to write it all out, as AndrewC suggested, patiently, step by step. ping me if you have troubles with this, and I'll add it into the answer. – Will Ness Jul 23 '15 at 21:10
  • f x = x + 1 is a normal function, right? f = \x + 1 is a curried function? – user1685095 Jul 23 '15 at 21:23
  • no, both are the same. (you meant, `f = \x -> x + 1` I presume). – Will Ness Jul 23 '15 at 21:28
  • `Applicative` is essential, rather than merely convenient, because of `traverse`, certain `replicateM`-like functions, and possibly some other things. What makes `Monad` essential? `MMorph`? – dfeuer Oct 08 '16 at 03:39
  • `join [replicate 5 x | x <- xs] = [ y | x <- xs, y <- replicate 5 x] = xs <* [(),(),(),(),()]` is *essentially* Applicative, `[ y | x <- xs, y <- replicate x x] = ?` is *essentially* Monadic. *Essentially* monadic is a computation that can't be constructed ahead of time. If we see Functors as computations / programs, Applicative is like a program that can be compiled ahead of time and *then* run; for Monadic programs the compiler (interpreter) must be present at their run time. So if we can't do without bind, it's Monadic. Something like that? – Will Ness Oct 08 '16 at 11:08