Here is a piece of code from the book "Learn You a Haskell for Great Good!" by Miran Lipovača:
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
return (a+b)
What is bound to the parameter b in the expression b <- (+10) in the do-expression? If it were a value like Just 7 or [7], the seven would get bound; if it were a call of getLine, then it would be a string. What about functions? Or is it incorrect to view such instructions with functions in terms of extracting and wrapping something? And a more general question. If monadic values are values with context, then what is considered the context in the case of functions?
2 Answers 2
If it were a value like
Just 7or[7], the seven would get bound
Okay, sure. But what if it were a value like [7,8,9]? Then there's no good, single answer to what value gets bound. In a somewhat related objection, you say "if it were a call of getLine, then it would be a string" -- but which string? There's no good, single answer to which string gets bound when I write b <- getLine, because the value that b takes on will depend on something the user does. So you will have to abandon this idea for understanding monads, and admit that they can have more structure and excitement than simply binding predictable values.
We are in the function monad case, and are defining a function named addStuff. The more exciting, structured answer to "what gets bound?" is that the value that gets bound depends on what value gets provided as an argument to addStuff. For each possible input that could be provided, there is a corresponding value that will appear in b. If addStuff is applied to 73, then b will be bound to 73+10=83; or if addStuff is applied to -5, then b will be bound to -5+10=5.
If monadic values are values with context, then what is considered the context in the case of functions?
In the case of functions, the argument to the function is the context.
Comments
What is bound to the parameter
b
The result of the function (at the right of the arrow, so here (+10)) and the input to the function addStuff, so if you call addStuff 5, b will be 15.
Indeed, if we desugar the do block, what you wrote is:
addStuff :: Int -> Int
addStuff = (*2) >>= \a -> ((+10) >>= \b -> return (a + b))
For the function instance of a Monad, we have:
-- | @since base-2.01 instance Applicative ((->) r) where pure = const f g x = f x (g x) liftA2 q f g x = q (f x) (g x) -- | @since base-2.01 instance Monad ((->) r) where f >>= k = \r -> k (f r) r
so that means for addStuff, it looks like:
addStuff = \r -> (\a -> (+10) >>= \b -> return (a+b)) ((*2) r) r
addStuff = \r -> (\a -> (\s -> (\b -> const (a+b)) ((+10) s) s)) ((*2) r) r
We can now do some function applications reducing the "noise":
addStuff = \r -> (\a -> (\s -> (\b -> const (a+b)) ((+10) s) s)) ((*2) r) r
addStuff = \r -> (\b -> const (((*2) r) +b)) ((+10) r) r
addStuff = \r -> (const (((*2) r) +((+10) r))) r
addStuff = \r -> (((*2) r) +((+10) r))
it thus each time passes the argument r further, and the return on the last line ignores the parameter.
We could have used:
addStuff :: Int -> Int
addStuff = do
a <- (*2)
b <- (+10)
((a+b)+)
This would have added the input to the a+b part, so addStuff 5 is then 30 since we first multiply 5 with 2 for a, then 5+10 for b, and finally we add the input a third time.
Comments
Explore related questions
See similar questions with these tags.
Int -> Int, the(Int ->)is the context, theIntis the value. But really "monadic values are values with context" doesn't make much sense in many cases. It's the type (or structure) that is monadic, and what a concrete value means depends very much on that type.donotation to a>>=chain, and then look up the implementation of>>=andreturnfor functions.Maybe, it's not that simple. What doesx <- Nothingbind tox? Then answer is "nothing" (notNothing); the very idea ofxhaving a value is irrelevant, because whatever logic that would have usedxis ignored and replaced itself byNothing.x <- Nothingbind to x?" If the bind operator (>>=) getsNothing(or an empty list, for example) it "doesn't try to extract and bind" anything, it just passes theNothingonward, probably for the next bind operator to deal with it.