Question about example in 'using do notation with Writer' section of LYH

Hello List, Chapter 14 of LYH ( http://learnyouahaskell.com/for-a-few-monads-more#reader ) has the following bit of code: import Control.Monad.Writer logNumber :: Int -> Writer [String] Int logNumber x = Writer (x, ["Got number: " ++ show x]) multWithLog :: Writer [String] Int multWithLog = do a <- logNumber 3 b <- logNumber 5 return (a*b) I have a fair grasp of what's going bu the last line return(a*b) eludes me. Specifically its the a*b part that baffles me. I think 3 is bound to 'a' and 5 to 'b', then return(a*b) would put the value 15 in a context ... So what becomes of the string parts of the Writer monads created by logNumber 3 and logNumber 5 respectively. Regards, - Olumide

Nothing happens to them. They are still in there. Whereas logNumber always returns the number you give it, multiWithLog always returns 15 and appends two strings into its state ("got number 3 and 5"). You can return whatever value you want from a Monad. In this case he's doing a * b, but you could do 2 * b, Just (a + b) or anything else. As an example, here I return a tuple instead. multWithLogTuple :: Writer [String] (Int,Int,Int) multWithLogTuple = do a <- logNumber 3 b <- logNumber 5 return (a,b,2*b)
runWriter multWithLogTuple ((3,5,10),["Got number: 3","Got number: 5"])
On Thu, Jan 26, 2017 at 10:41 AM, Olumide <50295@web.de> wrote:
Hello List,
Chapter 14 of LYH ( http://learnyouahaskell.com/for-a-few-monads-more#reader ) has the following bit of code:
import Control.Monad.Writer
logNumber :: Int -> Writer [String] Int logNumber x = Writer (x, ["Got number: " ++ show x])
multWithLog :: Writer [String] Int multWithLog = do a <- logNumber 3 b <- logNumber 5 return (a*b)
I have a fair grasp of what's going bu the last line return(a*b) eludes me. Specifically its the a*b part that baffles me. I think 3 is bound to 'a' and 5 to 'b', then return(a*b) would put the value 15 in a context ... So what becomes of the string parts of the Writer monads created by logNumber 3 and logNumber 5 respectively.
Regards,
- Olumide _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

On 26/01/2017 16:02, David McBride wrote:
Nothing happens to them. They are still in there. Whereas logNumber always returns the number you give it, multiWithLog always returns 15 and appends two strings into its state ("got number 3 and 5").
You can return whatever value you want from a Monad. In this case he's doing a * b, but you could do 2 * b, Just (a + b) or anything else. As an example, here I return a tuple instead.
multWithLogTuple :: Writer [String] (Int,Int,Int) multWithLogTuple = do a <- logNumber 3 b <- logNumber 5 return (a,b,2*b)
So return (a*b) still creates Writer [String] Int? I thought the [String] parts of 'a' and 'b' ought to be in the result but I see it needn't. - Olumide

Fully expanding your program might help. One of the great things about Haskell is equational reasoning: if two things have been declared equal, you can substitute one for the other. First, let's desugar that do notation to the equivalent bind chain: multWithLog = logNumber 3 >>= \a -> logNumber 5 >>= \b -> return (a*b) Evaluate the logNumber and return calls to normal form from their definitions, also considering the monoid definitions of (++) and mempty for lists: multWithLog = Writer (3, ["Got number: 3"]) >>= \a -> Writer (5, ["Got number: 5"]) >>= \b -> Writer (a*b, []) Now, refer to the definition of (>>=) for Writer (as shown in LYAH): (Writer (x, v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v') Rewritten as a lamba (and replacing `mappend` with (++) for brevity), this becomes: \(Writer (x, v)) f -> let (Writer (y, v')) = f x in Writer (y, v++v') It's no longer an infix function, so we'll have to shift things around a little. Here's what it expands to: multWithLog = (\(Writer (x, v)) f -> -- \ let (Writer (y, v')) = f x -- | bind in Writer (y, v++v')) -- / (Writer (3, ["Got number: 3"])) -- Writer (x, v) (\a -> -- f (\(Writer (x2, v2)) f2 -> -- \ let (Writer (y2, v2')) = f2 x2 -- | bind in Writer (y2, v2++v2')) -- / (Writer (5, ["Got number: 5"])) -- Writer (x2, v2) (\b -> Writer (a*b, [])) -- f2 ) -- (end f) Now it's just a matter of simplification. Let's start by eliminating the first argument of each bind, i.e. (Writer (x,v)), by substituting with concrete values. multWithLog = (\f -> -- \ partially let (Writer (y, v')) = f 3 -- | applied in Writer (y, ["Got number: 3"]++v')) -- / bind (\a -> -- f (\f2 -> -- \ partially let (Writer (y2, v2')) = f2 5 -- | applied in Writer (y2, ["Got number: 5"]++v2')) -- / bind (\b -> Writer (a*b, [])) -- f2 ) -- (end f) Substitute f2, and eliminate both \f2 and \b in the same way: multWithLog = (\f -> let (Writer (y, v')) = f 3 in Writer (y, ["Got number: 3"]++v')) (\a -> let (Writer (y2, v2')) = Writer (a*5, []) -- applied f2 in Writer (y2, ["Got number: 5"]++v2') ) With a full match on the inner let block, that can also be eliminated: multWithLog = (\f -> let (Writer (y, v')) = f 3 in Writer (y, ["Got number: 3"]++v')) (\a -> Writer (a*5, ["Got number: 5"]++[])) I'll forego the last few substitutions. If it's still not clear how you get to the final output, you should run through them yourself. You'll eventually reach a static definition of the result -- which shouldn't really be surprising, since there were no arguments to this "function" and its type doesn't contain -> anywhere. :) On Thu, Jan 26, 2017 at 1:34 PM, Olumide <50295@web.de> wrote:
On 26/01/17 16:02, David McBride wrote:
runWriter multWithLogTuple
((3,5,10),["Got number: 3","Got number: 5"])
On second thoughts I don't think I understand how the logs are concatenated. I was expecting (15,["Got number: 15") in the original example.
- Olumide _______________________________________________ Beginners mailing list Beginners@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners

On 27/01/2017 08:34, Theodore Lief Gannon wrote:
Fully expanding your program might help. One of the great things about Haskell is equational reasoning: if two things have been declared equal, you can substitute one for the other.
First, let's desugar that do notation to the equivalent bind chain:
multWithLog = logNumber 3 >>= \a -> logNumber 5 >>= \b -> return (a*b)
Evaluate the logNumber and return calls to normal form from their definitions, also considering the monoid definitions of (++) and mempty for lists:
multWithLog = Writer (3, ["Got number: 3"]) >>= \a -> Writer (5, ["Got number: 5"]) >>= \b -> Writer (a*b, [])
Now, refer to the definition of (>>=) for Writer (as shown in LYAH):
(Writer (x, v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
Thank you for your explanation. The substitutions helped a lot even though I wasn't able to follow the equational reasoning. The expansion gave me enough starting information to figure out what's going on by starting from the innermost bind. BTW, I thought the definition of (>>=) was supposed to come from Control.Monad.Writer. Regards, - Olumide

what becomes of the string parts of the Writer monads
multWithLog keeps [String] and outputs Int to fully benefit from Writer [String] Int, you'd call e.g. runWriter or execWriter similar to other *State state out* monads, *m state out* lets you get/set/keep state as needed without passing it as an arg. *state* is there if you need it.
participants (4)
-
David McBride
-
Imants Cekusins
-
Olumide
-
Theodore Lief Gannon