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;








2















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 mappending 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?










share|improve this question


























  • 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





    "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

















2















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 mappending 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?










share|improve this question


























  • 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





    "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













2












2








2








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 mappending 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?










share|improve this question
















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 mappending 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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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





    "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 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





    "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 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












2 Answers
2






active

oldest

votes


















2
















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.






share|improve this answer


































    0
















    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 wrappers




      it 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.






    share|improve this answer





























      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
      );



      );














      draft saved

      draft discarded
















      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









      2
















      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.






      share|improve this answer































        2
















        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.






        share|improve this answer





























          2














          2










          2









          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.






          share|improve this answer















          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          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


























              0
















              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 wrappers




                it 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.






              share|improve this answer































                0
















                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 wrappers




                  it 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.






                share|improve this answer





























                  0














                  0










                  0









                  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 wrappers




                    it 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.






                  share|improve this answer















                  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 wrappers




                    it 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.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  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































                      draft saved

                      draft discarded















































                      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.




                      draft saved


                      draft discarded














                      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





















































                      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







                      Popular posts from this blog

                      Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

                      Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

                      Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript