In order to refresh my 20 year old experience with Haskell I am walking through https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours/Adding_Variables_and_Assignment and at one point the following line is introduced to apply op
to all the params. This is to implement e.g. (+ 1 2 3 4)
numericBinop op params = mapM unpackNum params >>= return . Number . foldl1 op
I do not understand the syntax, and the explanation in the text is a bit vague.
I understand what foldl1
does and how to dot functions (unpackNum
is a helper function), but using Monads and the >>=
operator leaves me a bit confused.
How is this to be read?
The slang term is a popular way to praise someone who is going above and beyond to do a good job. According to Urban Dictionary, "understood the assignment" means, "a phrase used when someone is giving it 110% ... Whether it's what they're doing, what they're wearing, someone who is really on top of their s***" .
The assignment may require you to persuade your reader, compare and contrast ideas, or summarize an author's point of view. Considering your purpose at this point will make it easier for you to figure out what kind of thesis you'll need when you start to write the paper.
Essentially,
mapM unpackNum params >>= return . Number . foldl1 op
is made of two parts.
mapM unpackNum params
means: take the list of parameters params
. On each item, apply unpackNum
: this will produce an Integer
wrapped inside the ThrowsError
monad. So, it's not exactly a plain Integer
, since it has a chance to error out. Anyway, using unpackNum
on each item either successfully produces all Integers
, or throws some error. In the first case, we produce a new list of type [Integer]
, in the second one we (unsurprisingly) throw the error. So, the resulting type for this part is ThrowsError [Integer]
.
The second part is ... >>= return . Number . foldl1 op
. Here >>=
means: if the first part threw some error, the whole expression throws that error as well. If the part succeeded in producing [Integer]
then proceed with foldl1 op
, wrap the result as a Number
, and finally use return
to inject this value as a successful computation.
Overall there are monadic computations, but you should not think about those too much. The monadic stuff here is only propagating the errors around, or store plain values if the computation is successful. With a bit of experience, one can concentrate on the successful values only, and let mapM,>>=,return
handle the error cases.
By the way, note that while the book uses code like action >>= return . f
, this is arguably a bad style. One can use, to the same effect, fmap f action
or f <$> action
, which is a more direct way to express the same computation. E.g.
Number . foldl1 op <$> mapM unpackNum params
which is very close to a non-monadic code which ignores the error cases
-- this would work if there was no monad around, and errors did not have to be handled
Number . foldl1 op $ map unpackNum params
Your question is about syntax, so I'll just talk about how to parse that expression. Haskell's syntax is pretty simple. Informally:
>>=
, or .
) are infix (i.e. their first argument is to the left of the identifier)infix...
declaration)So only knowing this, if I see:
mapM unpackNum params >>= return . Number . foldl1 op
To begin with I know that it must be parse like
(mapM unpackNum params) >>= return . Number . (foldl1 op)
To go further we need to inspect the fixity/precedence of the two operators we see in this expression:
Prelude> :info (.)
(.) :: (b -> c) -> (a -> b) -> a -> c -- Defined in ‘GHC.Base’
infixr 9 .
Prelude> :info (>>=)
class Applicative m => Monad (m :: * -> *) where
(>>=) :: m a -> (a -> m b) -> m b
...
-- Defined in ‘GHC.Base’
infixl 1 >>=
(.)
has a higher precedence (9
vs 1
for >>=
), so its arguments will bind more tightly (i.e. we parenthesize them first). But how do we know which of these is correct?
(mapM unpackNum params) >>= ((return . Number) . (foldl1 op))
(mapM unpackNum params) >>= (return . (Number . (foldl1 op)))
...? Because (.)
was declared infixr
it associates to the right, meaning the second parse above is correct.
As Will Ness points out in the comments, (.)
is associative (like e.g. addition) so both of these happen to be semantically equivalent.
With a little experience with a library (or the Prelude
in this case) you learn to parse expressions with operators correctly without thinking too much.
If after doing this exercise you want to understand what a function does or how it works, then you can click through to the source of the functions you're interested in and replace occurrences of left-hand sides with right-hand sides (i.e. inline the bodies of the functions and terms). Obviously you can do this in your head or in an editor.
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