[Haskell-beginners] Need help understanding the tell function in the Monad Writer example in LYAH

Francesco Ariis fa-ml at ariis.it
Fri Feb 3 13:54:20 UTC 2017


On Fri, Feb 03, 2017 at 11:31:32AM +0000, Olumide wrote:
> Hello List,
> 
> Would someone kindly explain how the tell function interacts with the Writer
> Monad as show below:
> (Example taken from chapter 14 of LYAH
> http://learnyouahaskell.com/for-a-few-monads-more#reader )
> 
> multWithLog :: Writer [String] Int
> multWithLog = do
> 	a <- logNumber 3
> 	b <- logNumber 5
> 	tell ["Gonna multiply these two"]
> 	return (a*b)
> 
> Result:
>     ghci> runWriter multWithLog
>     (15,["Got number: 3","Got number: 5","Gonna multiply these two"])
> 
> I know that tell function binds to an argument that is discarded but I don't
> know how its argument "Gonna multiply these two" is concatenated with the
> other the Writer created by logNumber 3 and logNumber 5.
> 
> Also, I don't understand the paragraph following the example:
> 
> "It's important that return (a*b) is the last line, because the result of
> the last line in a do expression is the result of the whole do expression.
> Had we put tell as the last line, () would have been the result of this do
> expression. We'd lose the result of the multiplication. However, the log
> would be the same."
> 
> 
> Regards,
> 
> - Olumide

Hello Olumide,
    a Writer do block can be read as a series of function which all have
a "hidden parameter". This parameter is the pile of log messages.
So you could as well substitute `tell ...` with

    myTell :: String -> Writer [String] ()
    myTell s = writer ((), [s])

and then in the do block

    -- ... receiving a list of log messages
    c <- myTell "something" -- adding mine to the list (and binding
                            -- a variable)
    return (a*b) -- c is not being used!
                 -- but the log message *is* there

You can verify this yourself by adding `logNumber` statement in a do
block and not using them in the last return statement. There too log
will appear even if the bound variable is unused.

    multWithLog :: Writer [String] Int
    multWithLog = do
 	    a <- logNumber 3
	    b <- logNumber 5 -- not used but logged
        -- equivalent to: logNumber 5 (without b <-)
 	    return (a)

> Also, I don't understand the paragraph following the example:
>
> "It's important that return (a*b) is the last line, because the result of
> the last line in a do expression is the result of the whole do expression.
> Had we put tell as the last line, () would have been the result of this do
> expression. We'd lose the result of the multiplication. However, the log
> would be the same."

`tell` is really not much different from `myTell`. Let's examine it again:

    myTell :: String -> Writer [String] ()
    myTell s = writer ((), [s])

See the ()? It means it is *actually* returning something, a ().
Remember that `return` isn't the same `return` as in some imperative
languages: it only wraps a value in the monad we are using:

    return 5
    -- takes `5` and 'lifts' so it is usable inside the Writer
    -- monad: `(5, [])`

Putting a `tell "something"` after a return statement would overwrite
that result (and gives us back a () instead).

Did this help?
My tip for really getting a Monad in your brain is to reimplement it.
It is a very useful exercise.
Also learning *not* to use the `do notation` helps too, as having
operators instead of magic makes things easier to understand.



More information about the Beginners mailing list