I am at a loss to understand recursion in a monad. From the haskell.org wiki here is an example:
main = f 3
f 0 = return []
f n = do v <- getLine
vs <- f (n-1)
return $! v : vs
This program gets three lines from standard input recursively. What I cannot comprehend is what happens when you get to f 0 and how the recursion unwinds. How does the final value of the do block get constructed? Why is the final return line called repeatedly in the recursion? I know return is not function return as in imperative languages, but I cannot see how this line gets repeated.
I am aware that this is a raw beginners question, but I am stumped.
In this particular case, you can just unfold completely. Perhaps this helps:
f 3
= { reduce f (and the subtraction) }
do v <- getLine
vs <- f 2
return $! v : vs
= { reduce f (and the subtraction) }
do v <- getLine
vs <- do v' <- getLine
vs' <- f 1
return $! v' : vs'
return $! v : vs
= { reduce f (and the subtraction) }
do v <- getLine
vs <- do v' <- getLine
vs' <- do v'' <- getLine
vs'' <- f 0
return $! v'' : vs''
return $! v' : vs'
return $! v : vs
= { reduce f }
do v <- getLine
vs <- do v' <- getLine
vs' <- do v'' <- getLine
vs'' <- return []
return $! v'' : vs''
return $! v' : vs'
return $! v : vs
=
...
At this point, there's no recursion left. All we've done is apply function definitions. From here, we can simplify further if we assume that the monad laws hold:
...
= { vs'' <- return [] means that vs'' is [] }
do v <- getLine
vs <- do v' <- getLine
vs' <- do v'' <- getLine
return $! v'' : []
return $! v' : vs'
return $! v : vs
= { inline the innermost do block }
do v <- getLine
vs <- do v' <- getLine
v'' <- getLine
vs' <- return $! v'' : []
return $! v' : vs'
return $! v : vs
= { inline return $! v'' : [] }
do v <- getLine
vs <- do v' <- getLine
v'' <- getLine
return $! v' : v'' : []
return $! v : vs
= { inline the innermost do block }
do v <- getLine
v' <- getLine
v'' <- getLine
vs <- return $! v' : v'' : []
return $! v : vs
= { inline return $! v' : v'' : [] }
do v <- getLine
v' <- getLine
v'' <- getLine
return $! v : v' : v'' : []
You can "pseudo-compile"/"unwind" the monadic block to see how it works:
f 3 = do v <- getLine
vs <- f 2 -- (will return a list with 2 lines from stdin
return $! v : vs
f 2 = do v <- getLine
vs <- f 1 -- (will return singleton list with a line from stdin)
return $! v : vs
f 1 = do v <- getLine
vs <- f 0 -- (will return an empty list)
return $! v : vs
f 0 = return []
So, it will getLine
3 times and then build up a list of the lines, the first line will be the first value in the list.
Every time you see a return
in a monadic expression, you are putting a value inside it. Every time you see a <-
(bind) in a monadic expression, you are taking values out of it. In the case of the IO
monad, you are always putting and taking out a single of the monad. In contrast, the list monad may "take out" (bind) consecutive values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With