<div dir="ltr"><div>You're asking very good questions, about a topic I considered bringing up in my previous response but didn't. I guess I will now :)</div><div><br></div><div>The important thing to realize about monad transformers (and monads in general) is that _they're nothing special_. Under the surface, WriterT is simply:</div><div><br></div><div>* Modifying your functions to have an extra value returned</div><div>* Providing convenience functions like `tell` and `liftIO` to work with that extra value</div><div><br></div><div>I would strongly advise getting used to writing code in both the transformer and non-transformer style to convince yourself that they are the same thing. To make this concrete, look at the WriterT definition:<br></div><div><br></div><div>    newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }</div><div><br></div><div>If you take away the newtype wrapping business, you can see that it's just saying `WriterT w m a = m (a, w)`. And therefore, if we go back to your types, we can replace:</div><div><br></div><div>    WriterT (Sum a) IO ()</div><div><br></div><div>with</div><div><br></div><div>    IO ((), Sum a)</div><div><br></div><div>In fact, that's exactly what `runWriterT` is doing. I would recommend trying to rewrite your code to not even bother with the `WriterT` at all and see if you can get the same output.</div><div><br></div><div>So your intuition is correct: there's no output parameter like in C. You could simulate that in Haskell by passing in a mutable variable (like an IORef), but that's not idiomatic. Passing back pairs of values is common in Haskell.</div><div><br></div><div>Transformers can be useful for avoiding a lot of boilerplate code. They can also introduce a lot of complexity in some cases. Figuring out when is the right time to use them and when not is subjective and nuanced. Knowing the skill is great; being able to avoid using the skill is also important :)<br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Jul 25, 2017 at 1:29 PM, Baa <span dir="ltr"><<a href="mailto:aquagnu@gmail.com" target="_blank">aquagnu@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Hello, Michael! This answers to my question completely, thank you so<br>
much!<br>
<br>
But looking at this code, I thought: how is it right to wrap/unwrap<br>
write monad? In languages like C or Python we can pass input/output<br>
argument (`int*` in C or `[0]` in Python) and use it as some<br>
accumulator. But here writer monad is not using as accumulator,<br>
accumulating (summation) happens in `mconcat`, right? It's using only<br>
as output value, i.e. place to "yield" result. I mean `w` is 0, each<br>
call of `runIt` sets there 1, after all calls we calculate sum of this<br>
1's. And instead of `censor (+1) w` I can do `tell 1` only.<br>
<br>
It means that `runIt` can return not `IO ()` but `IO Int` and results<br>
of all `runIt`'s asynchnronously gotten values can be accumulated with<br>
`mconcat` without using of writer monad. Am I right, writer monad here<br>
is not accumulator but only output value (like output arguments in<br>
C/C++/IDL/etc)? How is this a typical solution in Haskell - to use<br>
writer monad with wrap/unwrap multiple times, only to save output<br>
value?<br>
<div class="HOEnZb"><div class="h5"><br>
<br>
<br>
On Tue, 25 Jul 2017 12:31:56 +0300<br>
Michael Snoyman <<a href="mailto:michael@snoyman.com">michael@snoyman.com</a>> wrote:<br>
<br>
> Firstly, a direct answer to your question: use mconcat.<br>
><br>
>     main :: IO ()<br>
>     main = do<br>
>     let l = [1,2,3,4]<br>
>         w = writer ((), 0) :: WriterT (Sum Int) IO ()<br>
>     z <- sequence<br>
>         (map (A.async . runWriterT . runIt w) l)<br>
>         >>= mapM A.wait<br>
>     print $ snd $ mconcat z<br>
><br>
> Under the surface, WriterT is using mappend to combine the `Sum`<br>
> values anyway, so it's natural is `mconcat` (the version of mappend<br>
> that applies to list) to get the same result. Now some possible<br>
> improvements.<br>
><br>
> You're not actually using the return value from the `runIt` call,<br>
> just the writer value. There's a function called `execWriter` for<br>
> this:<br>
><br>
>     z <- sequence<br>
>         (map (A.async . execWriterT . runIt w) l)<br>
>         >>= mapM A.wait<br>
>     print $ mconcat z<br>
><br>
> Next, the combination of map and sequence can be written as traverse:<br>
><br>
>     z <- traverse (A.async . execWriterT . runIt w) l<br>
>         >>= mapM A.wait<br>
><br>
> But the async library is cool enough that it provides a function<br>
> called mapConcurrently that deals with the async/wait dance for you:<br>
><br>
>     main :: IO ()<br>
>     main = do<br>
>       let l = [1,2,3,4]<br>
>           w = writer ((), 0) :: WriterT (Sum Int) IO ()<br>
>       z <- A.mapConcurrently (execWriterT . runIt w) l<br>
>       print $ mconcat z<br>
><br>
> One final note: usage of `print` like this in a concurrent context<br>
> can run into interleaved output if you have the wrong buffer mode<br>
> turned out, leading to output like this:<br>
><br>
> 2<br>
> 3<br>
> 41<br>
><br>
> This is especially common when using runghc or ghci. You can either<br>
> change the buffering mode or use a different output function like<br>
> sayShow (from the say package, which I wrote):<br>
><br>
>     module Main where<br>
><br>
>     import qualified Control.Concurrent.Async as A<br>
>     import Control.Monad.Trans.Writer<br>
>     import Data.Monoid<br>
>     import Say<br>
><br>
>     runIt :: (Show a, Num a)<br>
>           => WriterT (Sum a) IO ()<br>
>           -> a<br>
>           -> WriterT (Sum a) IO ()<br>
>     runIt w x = do<br>
>       censor (+1) w -- emulates conditional count of something<br>
>       sayShow x<br>
><br>
>     main :: IO ()<br>
>     main = do<br>
>       let l = [1,2,3,4]<br>
>           w = writer ((), 0) :: WriterT (Sum Int) IO ()<br>
>       z <- A.mapConcurrently (execWriterT . runIt w) l<br>
>       sayShow $ mconcat z<br>
><br>
><br>
> On Tue, Jul 25, 2017 at 11:36 AM, Baa <<a href="mailto:aquagnu@gmail.com">aquagnu@gmail.com</a>> wrote:<br>
><br>
> > Hello, Dear List!<br>
> ><br>
> > There is package `async`<br>
> > (<a href="https://hackage.haskell.org/package/async-2.1.1.1/docs/" rel="noreferrer" target="_blank">https://hackage.haskell.org/<wbr>package/async-2.1.1.1/docs/</a><br>
> > Control-Concurrent-Async.html)<wbr>.<br>
> ><br>
> > Before, I had:<br>
> ><br>
> >     import qualified Control.Concurent.Async as A<br>
> >     ...<br>
> >     runIt :: ... -> IO ()<br>
> >     ...<br>
> >       sequence [A.async $ runIt ...] >>= mapM_ A.wait<br>
> ><br>
> > But now I want to count something inside `runIt`. I will use<br>
> > `Writer` monad for it (sure, it can be `State` also, not in<br>
> > principle to me). To do it synchronously, I done:<br>
> ><br>
> >     module Main where<br>
> ><br>
> >     import Control.Monad.Trans.Writer<br>
> >     import Control.Monad.IO.Class<br>
> >     import Data.Monoid<br>
> ><br>
> >     runIt :: (Show a, Num a) => WriterT (Sum a) IO () -> a -><br>
> > WriterT (Sum a) IO ()<br>
> >     runIt w x = do<br>
> >       censor (+1) w -- emulates conditional count of something<br>
> >       liftIO $ print x<br>
> ><br>
> >     main = do<br>
> >       let l = [1,2,3,4]<br>
> >           w = writer ((), 0) :: WriterT (Sum Int) IO ()<br>
> >       z <- runWriterT $ sequence [runIt w i | i <- l]<br>
> >       print $ snd z<br>
> ><br>
> > but now my `runIt` changes it's signature:<br>
> ><br>
> >     runIt :: Num a => WriterT (Sum a) IO () -> ... -> WriterT (Sum<br>
> > a) IO () ...<br>
> >       sequence [A.async $ runIt ...] >>= mapM_ A.wait<br>
> >                 ^^^^^^^^^^^<br>
> >                      ` ERROR is here!<br>
> ><br>
> > I get the error because `async`::IO () -> IO (A.Async ()) but I'm<br>
> > trying to pass it `WriterT (Sum a) IO ()`!<br>
> ><br>
> > To fix it I added `runWriterT` there:<br>
> ><br>
> >     res <- sequence [A.async $ runWriterT (runIt ...) ...] >>= mapM<br>
> > A.wait<br>
> ><br>
> > but now I will get list of counters, not one (res::[((), Sum Int)])!<br>
> ><br>
> > How to solve this problem: to run several actions asyncronously and<br>
> > to count something<br>
> > inside the action with `Writer` monad?<br>
> ><br>
> ><br>
> > ===<br>
> > Best regards, Paul<br>
> > ______________________________<wbr>_________________<br>
> > Beginners mailing list<br>
> > <a href="mailto:Beginners@haskell.org">Beginners@haskell.org</a><br>
> > <a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-<wbr>bin/mailman/listinfo/beginners</a><br>
> ><br>
<br>
______________________________<wbr>_________________<br>
Beginners mailing list<br>
<a href="mailto:Beginners@haskell.org">Beginners@haskell.org</a><br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-<wbr>bin/mailman/listinfo/beginners</a><br>
</div></div></blockquote></div><br></div>