Question is in bold at the bottom.
LYAH gives this example of using the do
notation with the Writer
monad
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int
logNumber x = writer (x, ["number " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
b <- logNumber 5
return (x*y)
where the definition can be re-written without the do
notation:
multWithLog = logNumber 3 >>= (\x ->
logNumber 5 >>= (\y ->
return (x*y)))
So far so good.
After that, the book introduces tell
, and edits the definition of multWithLog
like this:
multWithLog = do
a <- logNumber 3
b <- logNumber 5
tell ["something"]
return (x*y)
which again can be rewritten as:
multWithLog = logNumber 3 >>= (\x ->
logNumber 5 >>= (\y ->
tell ["something"] >>
return (x*y)))
Then the book makes the a point which appears unclear to me, if not inaccurate:
It's important that
return (a*b)
is the last line, because the result of the last line in ado
expression is the result of the whole do expression. Had we puttell
as the last line,()
would have been the result of thisdo
expression. We'd lose the result of the multiplication. However, the log would be the same.
Therefore, here my first doubt comes: if tell
results in ()
, then the code should not and does not even compile, as the ()
cannot match the expected type Int
, nor any other type other than ()
itself; so what is the author trying to tell us? To make this non-opinion-based, has something changed in Haskell, since the book was written, that made the above quoted statement unclear/inaccurate?
The equivalent re-write is further
multWithLog = logNumber 3 >>= (\ x ->
logNumber 5 >>= (\ y ->
tell ["something"] >>= (\ () -> -- () NB
return (x*y) >>= (\ result ->
return result ))))
and that is the ()
that tell ["something"]
"returns". Obviously, the shuffled
multWithLog2 = logNumber 3 >>= (\ x ->
logNumber 5 >>= (\ y ->
return (x*y) >>= (\ result ->
tell ["something"] >>= (\ () ->
return () ))))
would indeed have the type Writer [String] ()
, so if the signature were to specify Writer [String] Int
, it would indeed not compile.
Sans the type signature issues, the "log" i.e. the gathered [String]
list would be the same with both variants, as return
does not alter the collected output "log".