Question about the Writer monad as taught in LYAH. (How did the appending to the log take place?)Create a list using do syntax?Anatomy of a monad transformerHaskell IO exampleHaskell: <- in do notation for monadTesting Monadic CodeHow can I get the value of a Monad without System.IO.Unsafe?Haskell: Monads of monadMake Monoid instance of Writer (Haskell)Why does liftM not destroy context?Creating a Writer monad in Scala
Did slaves have slaves?
What is the source of "You can achieve a lot with hate, but even more with love" (Shakespeare?)
What if I don't know whether my program will be linked to a GPL library or not?
What is the origin of the "being immortal sucks" trope?
What does "boys rule, girls drool" mean?
Does Forgotten Realms setting count as “High magic”?
Should I inform my future product owner that there are big chances that a team member will leave the company soon?
Unpredictability of Stock Market
Python web-scraper to download table of transistor counts from Wikipedia
how to know this integral finite or infinite
Bit one of the Intel 8080's Flags register
How to make my “custom integer type” perform better?
What would happen if Protagoras v Euathlus were heard in court today?
What was the ultimate objective of The Party in 1984?
In Bb5 systems against the Sicilian, why does White exchange their b5 bishop without playing a6?
Asked to Not Use Transactions and to Use A Workaround to Simulate One
What is this WWII four-engine plane on skis?
Seven Places at Once - Another Google Earth Challenge?
Beauville-Laszlo for schemes
Are there objective criteria for classifying consonance v. dissonance?
Are there any “Third Order” acronyms used in space exploration?
How to give my students a straightedge instead of a ruler
What is a "major country" as named in Bernie Sanders' Healthcare debate answers?
Why is the year in this ISO timestamp not 2019?
Question about the Writer monad as taught in LYAH. (How did the appending to the log take place?)
Create a list using do syntax?Anatomy of a monad transformerHaskell IO exampleHaskell: <- in do notation for monadTesting Monadic CodeHow can I get the value of a Monad without System.IO.Unsafe?Haskell: Monads of monadMake Monoid instance of Writer (Haskell)Why does liftM not destroy context?Creating a Writer monad in Scala
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;
I'm learning Haskell from the "Learn you a Haskell for Great Good" tutorial and I've got to the part on writer monads. Here's the example that I can't figure out.
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) -- shouldn't return (3*5) result in (15,[]) ?
ghci> runWriter $ multWithLog
(15,["Got number: 3","Got number: 5"]) -- how did that happen?
I am trying understand how the monoidic value w
in the Writer w a
monad returned by the do
block got changed. The tutorial did not go into details on how the mappend
ing took place.
The type declaration for Writer
and the instance declaration for Writer
as a monad is given by the tutorial as
newtype Writer w a = Writer runWriter :: (a, w)
instance (Monoid w) => Monad (Writer w) where
return x = Writer (x, mempty)
(Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
if return x
results in Writer (x, mempty)
as per the instance declaration and mempty
for monoid [a]
is []
, shouldn't return (a*b)
, which amounts to return (3*5)
, evaluate to (15,[])
?
ghci> return (15) :: Writer [String] Int
WriterT (Identity (15,[]))
I gave the above command to ghci and it returns a WriterT
type value, the tuple contains an empty list as expected.
multWithLog :: Writer [String] Int
multWithLog = logNumber 3 >>= (a ->
logNumber 5 >>= (b ->
return (a*b)))
I've rewritten the do
block using bind operators instead. The above code gave identical result as the original code in the tutorial.
I'm under the impression that >>=
only extracted Int
3
from the result of logNumber 3
and gave it to (a -> logNumber 5 ...etc.)
, which then did the logNumber
function on a different value (5
) and so on. How did these operations lead to the [String]
part of the Writer monad being altered?
haskell monads monoids do-notation writer-monad
add a comment
|
I'm learning Haskell from the "Learn you a Haskell for Great Good" tutorial and I've got to the part on writer monads. Here's the example that I can't figure out.
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) -- shouldn't return (3*5) result in (15,[]) ?
ghci> runWriter $ multWithLog
(15,["Got number: 3","Got number: 5"]) -- how did that happen?
I am trying understand how the monoidic value w
in the Writer w a
monad returned by the do
block got changed. The tutorial did not go into details on how the mappend
ing took place.
The type declaration for Writer
and the instance declaration for Writer
as a monad is given by the tutorial as
newtype Writer w a = Writer runWriter :: (a, w)
instance (Monoid w) => Monad (Writer w) where
return x = Writer (x, mempty)
(Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
if return x
results in Writer (x, mempty)
as per the instance declaration and mempty
for monoid [a]
is []
, shouldn't return (a*b)
, which amounts to return (3*5)
, evaluate to (15,[])
?
ghci> return (15) :: Writer [String] Int
WriterT (Identity (15,[]))
I gave the above command to ghci and it returns a WriterT
type value, the tuple contains an empty list as expected.
multWithLog :: Writer [String] Int
multWithLog = logNumber 3 >>= (a ->
logNumber 5 >>= (b ->
return (a*b)))
I've rewritten the do
block using bind operators instead. The above code gave identical result as the original code in the tutorial.
I'm under the impression that >>=
only extracted Int
3
from the result of logNumber 3
and gave it to (a -> logNumber 5 ...etc.)
, which then did the logNumber
function on a different value (5
) and so on. How did these operations lead to the [String]
part of the Writer monad being altered?
haskell monads monoids do-notation writer-monad
I think you might be confused about whatreturn
means in Haskell - which is pretty common, so don't feel bad about it. In most imperative languages areturn x
at the bottom of a function definition means that the result of the function isx
. Butreturn
in Haskell is just a function, and the actual value ofmultWithLog
is the expression obtained from evaluating that entiredo
block, not just the last line withreturn
. In particular the successive lines of thedo
block are connected by applications of>>=
, and it is this which appends the log values "behind the scenes".
– Robin Zigmond
Mar 28 at 13:24
1
"I'm under the impression that >>= only extracts Int 3 ...". That's where you are wrong. Only 3 is passed as the argument to the next function, butm >>= f
doesn't simply return whateverf
returns. It creates a new Writer value using bothm
and the return value off
.
– chepner
Mar 28 at 14:56
add a comment
|
I'm learning Haskell from the "Learn you a Haskell for Great Good" tutorial and I've got to the part on writer monads. Here's the example that I can't figure out.
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) -- shouldn't return (3*5) result in (15,[]) ?
ghci> runWriter $ multWithLog
(15,["Got number: 3","Got number: 5"]) -- how did that happen?
I am trying understand how the monoidic value w
in the Writer w a
monad returned by the do
block got changed. The tutorial did not go into details on how the mappend
ing took place.
The type declaration for Writer
and the instance declaration for Writer
as a monad is given by the tutorial as
newtype Writer w a = Writer runWriter :: (a, w)
instance (Monoid w) => Monad (Writer w) where
return x = Writer (x, mempty)
(Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
if return x
results in Writer (x, mempty)
as per the instance declaration and mempty
for monoid [a]
is []
, shouldn't return (a*b)
, which amounts to return (3*5)
, evaluate to (15,[])
?
ghci> return (15) :: Writer [String] Int
WriterT (Identity (15,[]))
I gave the above command to ghci and it returns a WriterT
type value, the tuple contains an empty list as expected.
multWithLog :: Writer [String] Int
multWithLog = logNumber 3 >>= (a ->
logNumber 5 >>= (b ->
return (a*b)))
I've rewritten the do
block using bind operators instead. The above code gave identical result as the original code in the tutorial.
I'm under the impression that >>=
only extracted Int
3
from the result of logNumber 3
and gave it to (a -> logNumber 5 ...etc.)
, which then did the logNumber
function on a different value (5
) and so on. How did these operations lead to the [String]
part of the Writer monad being altered?
haskell monads monoids do-notation writer-monad
I'm learning Haskell from the "Learn you a Haskell for Great Good" tutorial and I've got to the part on writer monads. Here's the example that I can't figure out.
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) -- shouldn't return (3*5) result in (15,[]) ?
ghci> runWriter $ multWithLog
(15,["Got number: 3","Got number: 5"]) -- how did that happen?
I am trying understand how the monoidic value w
in the Writer w a
monad returned by the do
block got changed. The tutorial did not go into details on how the mappend
ing took place.
The type declaration for Writer
and the instance declaration for Writer
as a monad is given by the tutorial as
newtype Writer w a = Writer runWriter :: (a, w)
instance (Monoid w) => Monad (Writer w) where
return x = Writer (x, mempty)
(Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
if return x
results in Writer (x, mempty)
as per the instance declaration and mempty
for monoid [a]
is []
, shouldn't return (a*b)
, which amounts to return (3*5)
, evaluate to (15,[])
?
ghci> return (15) :: Writer [String] Int
WriterT (Identity (15,[]))
I gave the above command to ghci and it returns a WriterT
type value, the tuple contains an empty list as expected.
multWithLog :: Writer [String] Int
multWithLog = logNumber 3 >>= (a ->
logNumber 5 >>= (b ->
return (a*b)))
I've rewritten the do
block using bind operators instead. The above code gave identical result as the original code in the tutorial.
I'm under the impression that >>=
only extracted Int
3
from the result of logNumber 3
and gave it to (a -> logNumber 5 ...etc.)
, which then did the logNumber
function on a different value (5
) and so on. How did these operations lead to the [String]
part of the Writer monad being altered?
haskell monads monoids do-notation writer-monad
haskell monads monoids do-notation writer-monad
edited Mar 31 at 10:37
Will Ness
49.6k4 gold badges73 silver badges135 bronze badges
49.6k4 gold badges73 silver badges135 bronze badges
asked Mar 28 at 13:01
AndyAndy
111 bronze badge
111 bronze badge
I think you might be confused about whatreturn
means in Haskell - which is pretty common, so don't feel bad about it. In most imperative languages areturn x
at the bottom of a function definition means that the result of the function isx
. Butreturn
in Haskell is just a function, and the actual value ofmultWithLog
is the expression obtained from evaluating that entiredo
block, not just the last line withreturn
. In particular the successive lines of thedo
block are connected by applications of>>=
, and it is this which appends the log values "behind the scenes".
– Robin Zigmond
Mar 28 at 13:24
1
"I'm under the impression that >>= only extracts Int 3 ...". That's where you are wrong. Only 3 is passed as the argument to the next function, butm >>= f
doesn't simply return whateverf
returns. It creates a new Writer value using bothm
and the return value off
.
– chepner
Mar 28 at 14:56
add a comment
|
I think you might be confused about whatreturn
means in Haskell - which is pretty common, so don't feel bad about it. In most imperative languages areturn x
at the bottom of a function definition means that the result of the function isx
. Butreturn
in Haskell is just a function, and the actual value ofmultWithLog
is the expression obtained from evaluating that entiredo
block, not just the last line withreturn
. In particular the successive lines of thedo
block are connected by applications of>>=
, and it is this which appends the log values "behind the scenes".
– Robin Zigmond
Mar 28 at 13:24
1
"I'm under the impression that >>= only extracts Int 3 ...". That's where you are wrong. Only 3 is passed as the argument to the next function, butm >>= f
doesn't simply return whateverf
returns. It creates a new Writer value using bothm
and the return value off
.
– chepner
Mar 28 at 14:56
I think you might be confused about what
return
means in Haskell - which is pretty common, so don't feel bad about it. In most imperative languages a return x
at the bottom of a function definition means that the result of the function is x
. But return
in Haskell is just a function, and the actual value of multWithLog
is the expression obtained from evaluating that entire do
block, not just the last line with return
. In particular the successive lines of the do
block are connected by applications of >>=
, and it is this which appends the log values "behind the scenes".– Robin Zigmond
Mar 28 at 13:24
I think you might be confused about what
return
means in Haskell - which is pretty common, so don't feel bad about it. In most imperative languages a return x
at the bottom of a function definition means that the result of the function is x
. But return
in Haskell is just a function, and the actual value of multWithLog
is the expression obtained from evaluating that entire do
block, not just the last line with return
. In particular the successive lines of the do
block are connected by applications of >>=
, and it is this which appends the log values "behind the scenes".– Robin Zigmond
Mar 28 at 13:24
1
1
"I'm under the impression that >>= only extracts Int 3 ...". That's where you are wrong. Only 3 is passed as the argument to the next function, but
m >>= f
doesn't simply return whatever f
returns. It creates a new Writer value using both m
and the return value of f
.– chepner
Mar 28 at 14:56
"I'm under the impression that >>= only extracts Int 3 ...". That's where you are wrong. Only 3 is passed as the argument to the next function, but
m >>= f
doesn't simply return whatever f
returns. It creates a new Writer value using both m
and the return value of f
.– chepner
Mar 28 at 14:56
add a comment
|
2 Answers
2
active
oldest
votes
From the code you posted
(Writer (x,v)) >>= f =
let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
we can see that indeed f
is being called only with the x
argument.
So in logNumber 3 >>= a -> ...
variable a
indeed is bound to 3
.
However, >>=
does something after calling f
, namely it combines v
with v'
. In your example, v
is the [String]
coming from logNumber 3
which is ["Got number: 3"]
. Instead v'
comes form evaluating a -> ...
with a=3
, and is ["Got number: 5"]
.
mappend
for lists is ++
, which concatenates the lists together. Hence we get the final result.
Allow me to be a little sloppy and neglect the Writer
wrappers. We get
return (a*b)
= (a*b, [])
logNumber 5 >>= b -> return (a*b)
= logNumber 5 >>= b -> (a*b, [])
= (5, ["Got number: 5"]) >>= b -> (a*b, [])
= (a*5, ["Got number: 5"] `mappend` [])
= (a*5, ["Got number: 5"])
logNumber 3 >>= a -> logNumber 5 >>= b -> return (a*b)
= logNumber 3 >>= a -> (a*5, ["Got number: 5"])
= (3, ["Got number: 3"]) >>= a -> (a*5, ["Got number: 5"])
= (3*5, ["Got number: 3"] `mappend` ["Got number: 5"])
= (15, ["Got number: 3", "Got number: 5"])
Intuitively, we can pretend that a value in your writer monad is an effectful computation, which returns a value (like 3
) and as a side effect appends a few messages to a list-of-strings. The log of all such messages is invisible inside the monad (we can only append to the log), and will only be made available at the very end, when we will use runWriter
to exit from the monadic context.
add a comment
|
This ought to explain it:
> runWriter (return 15) :: (Int, [String])
(15,[]) -- == runWriter $ writer (15, mempty)
> runWriter (logNumber 3)
(3,["Got number: 3"]) -- == runWriter $ writer (3, ["Got number: 3"])
> runWriter (logNumber 5)
(5,["Got number: 5"]) -- == runWriter $ writer (5, ["Got number: 5"])
> runWriter (logNumber 3 >> logNumber 5)
(5,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"]
> runWriter (logNumber 3 >> logNumber 5 >> return 15 )
(15,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"] ++ []
> runWriter (logNumber 3 >>= (_ -> logNumber 5 >>= (_ -> return 15 ) ) )
(15,["Got number: 3","Got number: 5"])
> runWriter (logNumber 3 >>= (i -> logNumber 5 >>= (j -> return (i*j) ) ) )
(15,["Got number: 3","Got number: 5"])
And the last line's monadic expression is equivalent to multWithLog
's do
block.
Noice the nesting of the lambda functions: the lambda function
(j -> return (i*j) )
is situated inside the lambda function
(i -> logNumber 5 >>= (j -> return (i*j) ) )
That's why the i
in that return (i*j)
refers to the outer lambda function's argument i
, received by it from the outermost monadic action expression, logNumber 3
.
How? Because according to the definition of >>=
as you quote it in your question, we have
runWriter ( Writer (x,v) >>= f )
=
runWriter ( let (Writer (y, u)) = f x in Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in runWriter ( Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in (y, v `mappend` u)
i.e.
runWriter ( logNumber 5 >>= (j -> return j) )
= -------- f -----
runWriter ( writer (5, ["Got number: 5"]) >>= (j -> writer (j, mempty)) )
= -- x ------- v ------- -------- f ---------------
let Writer (y, u) = ( (j -> writer (j, mempty)) 5 )
-------- f --------------- x
in (y, ["Got number: 5"] `mappend` u)
= ------- v -------
let (y, u) = (5, mempty) in (y, ["Got number: 5"] `mappend` u)
=
(5, ["Got number: 5"] `mappend` mempty)
The "monoidic values" from each Writer
action do not "get changed". Each action contributes its "monoidic value" into the overall "monoidic value" of the combined Writer
-type computation of the do
block, built from its Writer
-type sub-computations by mappending
the monoidic values contributed by each sub-computation (the semantics of Writer
), found in the snd
field of the tuples representing the actions (the implementation of Writer
).
Again, this overall value is combined by combining the monoidic value parts of each tuple, namely, their snd
fields. The monoidic combination is mappend
, which is done behind the scenes for us by the Writer
type computations.
And for lists, mappend [a] [b] == [a] ++ [b] == [a,b]
, while mappend [a,b] [] == [a,b] ++ [] == [a,b]
.
Your questions, then:
shouldn't
return (a*b)
amount to(15,[])
?it should, and it does, as we saw at the start of the answer.
Writer
vs.WriterT
wrappersit doesn't matter. Both are isomorphic, because
Identity
is a no-op.WriterT
is part of monad-transformers implementation of Writer monad; the one given in the book is simpler and easier to follow.
How did these operations lead to the
[String]
part of the Writer monad being altered?not altered, but combined, by the
mappend
of the specific Monoid used by the specific Writer; as part of the monadic combination i.e. the monadic bind's,>>=
, definition; being as Monads generalize function call protocol and Writer Monad's generalization is collecting a Monoid values behind the scenes, so they can be appended in the shadows, in addition to the user functions doing their work in the open:do a <- logNumber 3
; b <- logNumber 5
; return (a*b)
= ----- user area ------ ---- hidden area ---
do a <- writer (3 , ["Got number: 3"] )
; b <- writer (5 , ["Got number: 5"] )
; writer (
(a*b) , [] )
=
Writer
( (3*5) , mconcat [..., ..., ...] )
Embrace do
-notation, it's your friend. It helps us think abstractly.
add a comment
|
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55398338%2fquestion-about-the-writer-monad-as-taught-in-lyah-how-did-the-appending-to-the%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
From the code you posted
(Writer (x,v)) >>= f =
let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
we can see that indeed f
is being called only with the x
argument.
So in logNumber 3 >>= a -> ...
variable a
indeed is bound to 3
.
However, >>=
does something after calling f
, namely it combines v
with v'
. In your example, v
is the [String]
coming from logNumber 3
which is ["Got number: 3"]
. Instead v'
comes form evaluating a -> ...
with a=3
, and is ["Got number: 5"]
.
mappend
for lists is ++
, which concatenates the lists together. Hence we get the final result.
Allow me to be a little sloppy and neglect the Writer
wrappers. We get
return (a*b)
= (a*b, [])
logNumber 5 >>= b -> return (a*b)
= logNumber 5 >>= b -> (a*b, [])
= (5, ["Got number: 5"]) >>= b -> (a*b, [])
= (a*5, ["Got number: 5"] `mappend` [])
= (a*5, ["Got number: 5"])
logNumber 3 >>= a -> logNumber 5 >>= b -> return (a*b)
= logNumber 3 >>= a -> (a*5, ["Got number: 5"])
= (3, ["Got number: 3"]) >>= a -> (a*5, ["Got number: 5"])
= (3*5, ["Got number: 3"] `mappend` ["Got number: 5"])
= (15, ["Got number: 3", "Got number: 5"])
Intuitively, we can pretend that a value in your writer monad is an effectful computation, which returns a value (like 3
) and as a side effect appends a few messages to a list-of-strings. The log of all such messages is invisible inside the monad (we can only append to the log), and will only be made available at the very end, when we will use runWriter
to exit from the monadic context.
add a comment
|
From the code you posted
(Writer (x,v)) >>= f =
let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
we can see that indeed f
is being called only with the x
argument.
So in logNumber 3 >>= a -> ...
variable a
indeed is bound to 3
.
However, >>=
does something after calling f
, namely it combines v
with v'
. In your example, v
is the [String]
coming from logNumber 3
which is ["Got number: 3"]
. Instead v'
comes form evaluating a -> ...
with a=3
, and is ["Got number: 5"]
.
mappend
for lists is ++
, which concatenates the lists together. Hence we get the final result.
Allow me to be a little sloppy and neglect the Writer
wrappers. We get
return (a*b)
= (a*b, [])
logNumber 5 >>= b -> return (a*b)
= logNumber 5 >>= b -> (a*b, [])
= (5, ["Got number: 5"]) >>= b -> (a*b, [])
= (a*5, ["Got number: 5"] `mappend` [])
= (a*5, ["Got number: 5"])
logNumber 3 >>= a -> logNumber 5 >>= b -> return (a*b)
= logNumber 3 >>= a -> (a*5, ["Got number: 5"])
= (3, ["Got number: 3"]) >>= a -> (a*5, ["Got number: 5"])
= (3*5, ["Got number: 3"] `mappend` ["Got number: 5"])
= (15, ["Got number: 3", "Got number: 5"])
Intuitively, we can pretend that a value in your writer monad is an effectful computation, which returns a value (like 3
) and as a side effect appends a few messages to a list-of-strings. The log of all such messages is invisible inside the monad (we can only append to the log), and will only be made available at the very end, when we will use runWriter
to exit from the monadic context.
add a comment
|
From the code you posted
(Writer (x,v)) >>= f =
let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
we can see that indeed f
is being called only with the x
argument.
So in logNumber 3 >>= a -> ...
variable a
indeed is bound to 3
.
However, >>=
does something after calling f
, namely it combines v
with v'
. In your example, v
is the [String]
coming from logNumber 3
which is ["Got number: 3"]
. Instead v'
comes form evaluating a -> ...
with a=3
, and is ["Got number: 5"]
.
mappend
for lists is ++
, which concatenates the lists together. Hence we get the final result.
Allow me to be a little sloppy and neglect the Writer
wrappers. We get
return (a*b)
= (a*b, [])
logNumber 5 >>= b -> return (a*b)
= logNumber 5 >>= b -> (a*b, [])
= (5, ["Got number: 5"]) >>= b -> (a*b, [])
= (a*5, ["Got number: 5"] `mappend` [])
= (a*5, ["Got number: 5"])
logNumber 3 >>= a -> logNumber 5 >>= b -> return (a*b)
= logNumber 3 >>= a -> (a*5, ["Got number: 5"])
= (3, ["Got number: 3"]) >>= a -> (a*5, ["Got number: 5"])
= (3*5, ["Got number: 3"] `mappend` ["Got number: 5"])
= (15, ["Got number: 3", "Got number: 5"])
Intuitively, we can pretend that a value in your writer monad is an effectful computation, which returns a value (like 3
) and as a side effect appends a few messages to a list-of-strings. The log of all such messages is invisible inside the monad (we can only append to the log), and will only be made available at the very end, when we will use runWriter
to exit from the monadic context.
From the code you posted
(Writer (x,v)) >>= f =
let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
we can see that indeed f
is being called only with the x
argument.
So in logNumber 3 >>= a -> ...
variable a
indeed is bound to 3
.
However, >>=
does something after calling f
, namely it combines v
with v'
. In your example, v
is the [String]
coming from logNumber 3
which is ["Got number: 3"]
. Instead v'
comes form evaluating a -> ...
with a=3
, and is ["Got number: 5"]
.
mappend
for lists is ++
, which concatenates the lists together. Hence we get the final result.
Allow me to be a little sloppy and neglect the Writer
wrappers. We get
return (a*b)
= (a*b, [])
logNumber 5 >>= b -> return (a*b)
= logNumber 5 >>= b -> (a*b, [])
= (5, ["Got number: 5"]) >>= b -> (a*b, [])
= (a*5, ["Got number: 5"] `mappend` [])
= (a*5, ["Got number: 5"])
logNumber 3 >>= a -> logNumber 5 >>= b -> return (a*b)
= logNumber 3 >>= a -> (a*5, ["Got number: 5"])
= (3, ["Got number: 3"]) >>= a -> (a*5, ["Got number: 5"])
= (3*5, ["Got number: 3"] `mappend` ["Got number: 5"])
= (15, ["Got number: 3", "Got number: 5"])
Intuitively, we can pretend that a value in your writer monad is an effectful computation, which returns a value (like 3
) and as a side effect appends a few messages to a list-of-strings. The log of all such messages is invisible inside the monad (we can only append to the log), and will only be made available at the very end, when we will use runWriter
to exit from the monadic context.
edited Mar 28 at 13:19
answered Mar 28 at 13:14
chichi
82.4k2 gold badges91 silver badges155 bronze badges
82.4k2 gold badges91 silver badges155 bronze badges
add a comment
|
add a comment
|
This ought to explain it:
> runWriter (return 15) :: (Int, [String])
(15,[]) -- == runWriter $ writer (15, mempty)
> runWriter (logNumber 3)
(3,["Got number: 3"]) -- == runWriter $ writer (3, ["Got number: 3"])
> runWriter (logNumber 5)
(5,["Got number: 5"]) -- == runWriter $ writer (5, ["Got number: 5"])
> runWriter (logNumber 3 >> logNumber 5)
(5,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"]
> runWriter (logNumber 3 >> logNumber 5 >> return 15 )
(15,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"] ++ []
> runWriter (logNumber 3 >>= (_ -> logNumber 5 >>= (_ -> return 15 ) ) )
(15,["Got number: 3","Got number: 5"])
> runWriter (logNumber 3 >>= (i -> logNumber 5 >>= (j -> return (i*j) ) ) )
(15,["Got number: 3","Got number: 5"])
And the last line's monadic expression is equivalent to multWithLog
's do
block.
Noice the nesting of the lambda functions: the lambda function
(j -> return (i*j) )
is situated inside the lambda function
(i -> logNumber 5 >>= (j -> return (i*j) ) )
That's why the i
in that return (i*j)
refers to the outer lambda function's argument i
, received by it from the outermost monadic action expression, logNumber 3
.
How? Because according to the definition of >>=
as you quote it in your question, we have
runWriter ( Writer (x,v) >>= f )
=
runWriter ( let (Writer (y, u)) = f x in Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in runWriter ( Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in (y, v `mappend` u)
i.e.
runWriter ( logNumber 5 >>= (j -> return j) )
= -------- f -----
runWriter ( writer (5, ["Got number: 5"]) >>= (j -> writer (j, mempty)) )
= -- x ------- v ------- -------- f ---------------
let Writer (y, u) = ( (j -> writer (j, mempty)) 5 )
-------- f --------------- x
in (y, ["Got number: 5"] `mappend` u)
= ------- v -------
let (y, u) = (5, mempty) in (y, ["Got number: 5"] `mappend` u)
=
(5, ["Got number: 5"] `mappend` mempty)
The "monoidic values" from each Writer
action do not "get changed". Each action contributes its "monoidic value" into the overall "monoidic value" of the combined Writer
-type computation of the do
block, built from its Writer
-type sub-computations by mappending
the monoidic values contributed by each sub-computation (the semantics of Writer
), found in the snd
field of the tuples representing the actions (the implementation of Writer
).
Again, this overall value is combined by combining the monoidic value parts of each tuple, namely, their snd
fields. The monoidic combination is mappend
, which is done behind the scenes for us by the Writer
type computations.
And for lists, mappend [a] [b] == [a] ++ [b] == [a,b]
, while mappend [a,b] [] == [a,b] ++ [] == [a,b]
.
Your questions, then:
shouldn't
return (a*b)
amount to(15,[])
?it should, and it does, as we saw at the start of the answer.
Writer
vs.WriterT
wrappersit doesn't matter. Both are isomorphic, because
Identity
is a no-op.WriterT
is part of monad-transformers implementation of Writer monad; the one given in the book is simpler and easier to follow.
How did these operations lead to the
[String]
part of the Writer monad being altered?not altered, but combined, by the
mappend
of the specific Monoid used by the specific Writer; as part of the monadic combination i.e. the monadic bind's,>>=
, definition; being as Monads generalize function call protocol and Writer Monad's generalization is collecting a Monoid values behind the scenes, so they can be appended in the shadows, in addition to the user functions doing their work in the open:do a <- logNumber 3
; b <- logNumber 5
; return (a*b)
= ----- user area ------ ---- hidden area ---
do a <- writer (3 , ["Got number: 3"] )
; b <- writer (5 , ["Got number: 5"] )
; writer (
(a*b) , [] )
=
Writer
( (3*5) , mconcat [..., ..., ...] )
Embrace do
-notation, it's your friend. It helps us think abstractly.
add a comment
|
This ought to explain it:
> runWriter (return 15) :: (Int, [String])
(15,[]) -- == runWriter $ writer (15, mempty)
> runWriter (logNumber 3)
(3,["Got number: 3"]) -- == runWriter $ writer (3, ["Got number: 3"])
> runWriter (logNumber 5)
(5,["Got number: 5"]) -- == runWriter $ writer (5, ["Got number: 5"])
> runWriter (logNumber 3 >> logNumber 5)
(5,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"]
> runWriter (logNumber 3 >> logNumber 5 >> return 15 )
(15,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"] ++ []
> runWriter (logNumber 3 >>= (_ -> logNumber 5 >>= (_ -> return 15 ) ) )
(15,["Got number: 3","Got number: 5"])
> runWriter (logNumber 3 >>= (i -> logNumber 5 >>= (j -> return (i*j) ) ) )
(15,["Got number: 3","Got number: 5"])
And the last line's monadic expression is equivalent to multWithLog
's do
block.
Noice the nesting of the lambda functions: the lambda function
(j -> return (i*j) )
is situated inside the lambda function
(i -> logNumber 5 >>= (j -> return (i*j) ) )
That's why the i
in that return (i*j)
refers to the outer lambda function's argument i
, received by it from the outermost monadic action expression, logNumber 3
.
How? Because according to the definition of >>=
as you quote it in your question, we have
runWriter ( Writer (x,v) >>= f )
=
runWriter ( let (Writer (y, u)) = f x in Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in runWriter ( Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in (y, v `mappend` u)
i.e.
runWriter ( logNumber 5 >>= (j -> return j) )
= -------- f -----
runWriter ( writer (5, ["Got number: 5"]) >>= (j -> writer (j, mempty)) )
= -- x ------- v ------- -------- f ---------------
let Writer (y, u) = ( (j -> writer (j, mempty)) 5 )
-------- f --------------- x
in (y, ["Got number: 5"] `mappend` u)
= ------- v -------
let (y, u) = (5, mempty) in (y, ["Got number: 5"] `mappend` u)
=
(5, ["Got number: 5"] `mappend` mempty)
The "monoidic values" from each Writer
action do not "get changed". Each action contributes its "monoidic value" into the overall "monoidic value" of the combined Writer
-type computation of the do
block, built from its Writer
-type sub-computations by mappending
the monoidic values contributed by each sub-computation (the semantics of Writer
), found in the snd
field of the tuples representing the actions (the implementation of Writer
).
Again, this overall value is combined by combining the monoidic value parts of each tuple, namely, their snd
fields. The monoidic combination is mappend
, which is done behind the scenes for us by the Writer
type computations.
And for lists, mappend [a] [b] == [a] ++ [b] == [a,b]
, while mappend [a,b] [] == [a,b] ++ [] == [a,b]
.
Your questions, then:
shouldn't
return (a*b)
amount to(15,[])
?it should, and it does, as we saw at the start of the answer.
Writer
vs.WriterT
wrappersit doesn't matter. Both are isomorphic, because
Identity
is a no-op.WriterT
is part of monad-transformers implementation of Writer monad; the one given in the book is simpler and easier to follow.
How did these operations lead to the
[String]
part of the Writer monad being altered?not altered, but combined, by the
mappend
of the specific Monoid used by the specific Writer; as part of the monadic combination i.e. the monadic bind's,>>=
, definition; being as Monads generalize function call protocol and Writer Monad's generalization is collecting a Monoid values behind the scenes, so they can be appended in the shadows, in addition to the user functions doing their work in the open:do a <- logNumber 3
; b <- logNumber 5
; return (a*b)
= ----- user area ------ ---- hidden area ---
do a <- writer (3 , ["Got number: 3"] )
; b <- writer (5 , ["Got number: 5"] )
; writer (
(a*b) , [] )
=
Writer
( (3*5) , mconcat [..., ..., ...] )
Embrace do
-notation, it's your friend. It helps us think abstractly.
add a comment
|
This ought to explain it:
> runWriter (return 15) :: (Int, [String])
(15,[]) -- == runWriter $ writer (15, mempty)
> runWriter (logNumber 3)
(3,["Got number: 3"]) -- == runWriter $ writer (3, ["Got number: 3"])
> runWriter (logNumber 5)
(5,["Got number: 5"]) -- == runWriter $ writer (5, ["Got number: 5"])
> runWriter (logNumber 3 >> logNumber 5)
(5,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"]
> runWriter (logNumber 3 >> logNumber 5 >> return 15 )
(15,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"] ++ []
> runWriter (logNumber 3 >>= (_ -> logNumber 5 >>= (_ -> return 15 ) ) )
(15,["Got number: 3","Got number: 5"])
> runWriter (logNumber 3 >>= (i -> logNumber 5 >>= (j -> return (i*j) ) ) )
(15,["Got number: 3","Got number: 5"])
And the last line's monadic expression is equivalent to multWithLog
's do
block.
Noice the nesting of the lambda functions: the lambda function
(j -> return (i*j) )
is situated inside the lambda function
(i -> logNumber 5 >>= (j -> return (i*j) ) )
That's why the i
in that return (i*j)
refers to the outer lambda function's argument i
, received by it from the outermost monadic action expression, logNumber 3
.
How? Because according to the definition of >>=
as you quote it in your question, we have
runWriter ( Writer (x,v) >>= f )
=
runWriter ( let (Writer (y, u)) = f x in Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in runWriter ( Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in (y, v `mappend` u)
i.e.
runWriter ( logNumber 5 >>= (j -> return j) )
= -------- f -----
runWriter ( writer (5, ["Got number: 5"]) >>= (j -> writer (j, mempty)) )
= -- x ------- v ------- -------- f ---------------
let Writer (y, u) = ( (j -> writer (j, mempty)) 5 )
-------- f --------------- x
in (y, ["Got number: 5"] `mappend` u)
= ------- v -------
let (y, u) = (5, mempty) in (y, ["Got number: 5"] `mappend` u)
=
(5, ["Got number: 5"] `mappend` mempty)
The "monoidic values" from each Writer
action do not "get changed". Each action contributes its "monoidic value" into the overall "monoidic value" of the combined Writer
-type computation of the do
block, built from its Writer
-type sub-computations by mappending
the monoidic values contributed by each sub-computation (the semantics of Writer
), found in the snd
field of the tuples representing the actions (the implementation of Writer
).
Again, this overall value is combined by combining the monoidic value parts of each tuple, namely, their snd
fields. The monoidic combination is mappend
, which is done behind the scenes for us by the Writer
type computations.
And for lists, mappend [a] [b] == [a] ++ [b] == [a,b]
, while mappend [a,b] [] == [a,b] ++ [] == [a,b]
.
Your questions, then:
shouldn't
return (a*b)
amount to(15,[])
?it should, and it does, as we saw at the start of the answer.
Writer
vs.WriterT
wrappersit doesn't matter. Both are isomorphic, because
Identity
is a no-op.WriterT
is part of monad-transformers implementation of Writer monad; the one given in the book is simpler and easier to follow.
How did these operations lead to the
[String]
part of the Writer monad being altered?not altered, but combined, by the
mappend
of the specific Monoid used by the specific Writer; as part of the monadic combination i.e. the monadic bind's,>>=
, definition; being as Monads generalize function call protocol and Writer Monad's generalization is collecting a Monoid values behind the scenes, so they can be appended in the shadows, in addition to the user functions doing their work in the open:do a <- logNumber 3
; b <- logNumber 5
; return (a*b)
= ----- user area ------ ---- hidden area ---
do a <- writer (3 , ["Got number: 3"] )
; b <- writer (5 , ["Got number: 5"] )
; writer (
(a*b) , [] )
=
Writer
( (3*5) , mconcat [..., ..., ...] )
Embrace do
-notation, it's your friend. It helps us think abstractly.
This ought to explain it:
> runWriter (return 15) :: (Int, [String])
(15,[]) -- == runWriter $ writer (15, mempty)
> runWriter (logNumber 3)
(3,["Got number: 3"]) -- == runWriter $ writer (3, ["Got number: 3"])
> runWriter (logNumber 5)
(5,["Got number: 5"]) -- == runWriter $ writer (5, ["Got number: 5"])
> runWriter (logNumber 3 >> logNumber 5)
(5,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"]
> runWriter (logNumber 3 >> logNumber 5 >> return 15 )
(15,["Got number: 3","Got number: 5"]) -- == ["Got number: 3"] ++ ["Got number: 5"] ++ []
> runWriter (logNumber 3 >>= (_ -> logNumber 5 >>= (_ -> return 15 ) ) )
(15,["Got number: 3","Got number: 5"])
> runWriter (logNumber 3 >>= (i -> logNumber 5 >>= (j -> return (i*j) ) ) )
(15,["Got number: 3","Got number: 5"])
And the last line's monadic expression is equivalent to multWithLog
's do
block.
Noice the nesting of the lambda functions: the lambda function
(j -> return (i*j) )
is situated inside the lambda function
(i -> logNumber 5 >>= (j -> return (i*j) ) )
That's why the i
in that return (i*j)
refers to the outer lambda function's argument i
, received by it from the outermost monadic action expression, logNumber 3
.
How? Because according to the definition of >>=
as you quote it in your question, we have
runWriter ( Writer (x,v) >>= f )
=
runWriter ( let (Writer (y, u)) = f x in Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in runWriter ( Writer (y, v `mappend` u) )
=
let (Writer (y, u)) = f x in (y, v `mappend` u)
i.e.
runWriter ( logNumber 5 >>= (j -> return j) )
= -------- f -----
runWriter ( writer (5, ["Got number: 5"]) >>= (j -> writer (j, mempty)) )
= -- x ------- v ------- -------- f ---------------
let Writer (y, u) = ( (j -> writer (j, mempty)) 5 )
-------- f --------------- x
in (y, ["Got number: 5"] `mappend` u)
= ------- v -------
let (y, u) = (5, mempty) in (y, ["Got number: 5"] `mappend` u)
=
(5, ["Got number: 5"] `mappend` mempty)
The "monoidic values" from each Writer
action do not "get changed". Each action contributes its "monoidic value" into the overall "monoidic value" of the combined Writer
-type computation of the do
block, built from its Writer
-type sub-computations by mappending
the monoidic values contributed by each sub-computation (the semantics of Writer
), found in the snd
field of the tuples representing the actions (the implementation of Writer
).
Again, this overall value is combined by combining the monoidic value parts of each tuple, namely, their snd
fields. The monoidic combination is mappend
, which is done behind the scenes for us by the Writer
type computations.
And for lists, mappend [a] [b] == [a] ++ [b] == [a,b]
, while mappend [a,b] [] == [a,b] ++ [] == [a,b]
.
Your questions, then:
shouldn't
return (a*b)
amount to(15,[])
?it should, and it does, as we saw at the start of the answer.
Writer
vs.WriterT
wrappersit doesn't matter. Both are isomorphic, because
Identity
is a no-op.WriterT
is part of monad-transformers implementation of Writer monad; the one given in the book is simpler and easier to follow.
How did these operations lead to the
[String]
part of the Writer monad being altered?not altered, but combined, by the
mappend
of the specific Monoid used by the specific Writer; as part of the monadic combination i.e. the monadic bind's,>>=
, definition; being as Monads generalize function call protocol and Writer Monad's generalization is collecting a Monoid values behind the scenes, so they can be appended in the shadows, in addition to the user functions doing their work in the open:do a <- logNumber 3
; b <- logNumber 5
; return (a*b)
= ----- user area ------ ---- hidden area ---
do a <- writer (3 , ["Got number: 3"] )
; b <- writer (5 , ["Got number: 5"] )
; writer (
(a*b) , [] )
=
Writer
( (3*5) , mconcat [..., ..., ...] )
Embrace do
-notation, it's your friend. It helps us think abstractly.
edited Mar 31 at 11:04
answered Mar 31 at 9:39
Will NessWill Ness
49.6k4 gold badges73 silver badges135 bronze badges
49.6k4 gold badges73 silver badges135 bronze badges
add a comment
|
add a comment
|
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55398338%2fquestion-about-the-writer-monad-as-taught-in-lyah-how-did-the-appending-to-the%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I think you might be confused about what
return
means in Haskell - which is pretty common, so don't feel bad about it. In most imperative languages areturn x
at the bottom of a function definition means that the result of the function isx
. Butreturn
in Haskell is just a function, and the actual value ofmultWithLog
is the expression obtained from evaluating that entiredo
block, not just the last line withreturn
. In particular the successive lines of thedo
block are connected by applications of>>=
, and it is this which appends the log values "behind the scenes".– Robin Zigmond
Mar 28 at 13:24
1
"I'm under the impression that >>= only extracts Int 3 ...". That's where you are wrong. Only 3 is passed as the argument to the next function, but
m >>= f
doesn't simply return whateverf
returns. It creates a new Writer value using bothm
and the return value off
.– chepner
Mar 28 at 14:56