<div dir="ltr">There's a thread from earlier this year where someone asked about a Monoid instance for ReaderT: <a href="https://mail.haskell.org/pipermail/haskell-cafe/2017-March/126588.html">https://mail.haskell.org/pipermail/haskell-cafe/2017-March/126588.html</a><div><br></div><div>Several other people showed interest in the instance, but in the end, the request was dismissed since there are other valid monoid instances for ReaderT. Conseqently, the PR was closed (<a href="https://hub.darcs.net/ross/transformers/issue/38">https://hub.darcs.net/ross/transformers/issue/38</a>). This final comment (<a href="https://mail.haskell.org/pipermail/haskell-cafe/2017-March/126605.html">https://mail.haskell.org/pipermail/haskell-cafe/2017-March/126605.html</a>) concludes with:</div><div><br>> In the absence of a principled reason to prefer one over the others and a<br>general consensus, I think it’s better not to choose.</div><div><br></div><div>This is the point I'd like to argue. There were three possible monoid instances for ReaderT discussed. They are:</div><div><br></div>    instance (Applicative m, Monoid a) => Monoid (ReaderT r m a)<div>    instance (Alternative m) => Monoid (ReaderT r m a)</div><div>    <span style="color:rgb(0,0,0);white-space:pre-wrap">instance (Monoid (m a)) => Monoid (ReaderT r m a)</span></div><div><span style="color:rgb(0,0,0);white-space:pre-wrap"><br></span></div><div><span style="color:rgb(0,0,0);white-space:pre-wrap">I believe there is good reason to prefer the first instance over the other two. The third instance requires FlexibleContexts. This takes it outside the realm of Haskell98 and Haskell2010, which the transformers library stays within. In fact, transformers pioneered the Eq1, Ord1, etc. classes that ended up in base specifically to avoid FlexibleContexts, and deviating from this now seems against the spirit of the library, since transformers intends that it is able to be built with any Haskell compiler, not just GHC.</span></div><div><span style="color:rgb(0,0,0);white-space:pre-wrap"><br></span></div><div><font color="#000000"><span style="white-space:pre-wrap">The Alternative-based instance is reasonable, but we already have another instance that for ReaderT that is implemented this way: the Alternative instance! Here it is:</span></font></div><div><font color="#000000"><span style="white-space:pre-wrap"><br></span></font></div><div><font color="#000000"><span style="white-space:pre-wrap">    instance Alternative m => Alternative (ReaderT r m)</span></font></div><div><font color="#000000"><span style="white-space:pre-wrap"><br></span></font></div><div><font color="#000000"><span style="white-space:pre-wrap">Here I'm going to loosely adapt an argument that Gabriel Gonzalez makes when defending his Monoid instance for ListT (<a href="https://www.reddit.com/r/haskell/comments/4r5bcj/listtransformer_a_beginnerfriendly_listt/d4z0i55/">https://www.reddit.com/r/haskell/comments/4r5bcj/listtransformer_a_beginnerfriendly_listt/d4z0i55/</a>)</span></font><span style="white-space:pre-wrap;color:rgb(0,0,0)">:</span></div><div><span style="white-space:pre-wrap;color:rgb(0,0,0)"><br></span></div>> Also, with the Alternative class the only thing you can do is concatenate collections (there's no way to write the cartesian product because the whole class is higher-kinded) so it's meaning is always unambiguous. However, with the Monoid class there are two possible behaviors (append or cartesian product). Given that the Alternative class has to be append, I prefer to give the Monoid class the other behavior. From the end user's perspective, if you use Alternative you always know exactly what you are getting so there is no disadvantage to using Alternative.<br>> The other reason that it's nice to give the Monoid class the other behavior is that it works really nicely when chaining monoid instances as described in this post.<div><br></div><div>The Alternative instance for ReaderT has to be Alternative-based. There is no way around this. Given that we have two possibilities for the Monoid instance, I would prefer to see the one that gives us something different from an instance we already have.</div><div><br></div><div>Why is it beneficial to have the different behavior instead of the existing behavior? In part, it's because we now have a greater number of behaviors from the typeclasses that most people are familiar with. Additionally, we actually end up in a better situation when the Monoid instance isn't what we need. Let's consider two scenarios to measure the inconvenience that a user faces when the Monoid instance isn't the one that they want:</div><div><br></div><div>1) The Monoid instance for ReaderT, WriterT, StateT, etc. is Monoid-based but we wish are in a scenario where we want the Alternative-based one.<br></div><div>2) The Monoid instance for ReaderT, WriterT, StateT, etc. is Alternative-based but we wish are in a scenario where we want the Monoid-based one.</div><div><br></div><div>In scenario (1), the workaround is trivial. In Data.Monoid, we have Alt, which recovers a Monoid instance from an Alternative instance:<br><br>    newtype Alt f a = Alt {getAlt :: f a}<br></div>    instance Alternative f => Monoid (Alt f a) where<div>      mempty = Alt empty</div><div>      mappend = coerce ((<|>) :: f a -> f a -> f a)</div><div><br></div><div>The same Alt data type is reusable with ReaderT, WriterT, and StateT to recover the Alternative-based instance we wanted:</div><div><br></div><div>    myActions :: [ReaderT IO ()]</div><div>    runUntilSuccess :: ReaderT IO ()</div><div>    runUntilSuccess = coerce (fold (coerce myActions :: [Alt (ReaderT IO) ()]))<br><div><br></div><div>But what about scenario (2)? Let's say that we now instead want the Monoid-based instance that requires all of the actions to succeed.</div><div><br></div><div>    myActions :: [ReaderT IO ()]</div><div>    runAll :: ReaderT IO ()</div><div>    runAll = ...</div><div><br></div><div>What do we put in place of the ellipsis? We can just write it out by hand, but it would be nicer if this Monoid instance was reusable in some way. Let's say that we introduce a newtype wrapper for the Monoid-based Monoid instance:</div><div><br></div><div>    newtype MonoidReaderT m a = MonoidReaderT ...</div><div>    instance (Applicative m, Monoid a) => Monoid (MonoidReaderT m a)</div><div><br></div><div>But now if we want something like this for StateT or WriterT, they're going to need their own newtype wrappers as well. That's unfortunate. In scenario (1), we needed a single newtype wrapper (that already exists in base!) to recover everything that we lost. In scenario (2), we need a newtype wrapper per type. So, by providing an instance that gives us more behaviors from haskell's blessed set of typeclasses, we end up in a better situation when we need to recover what we don't have.</div><div><br></div><div>Finally, and this is the weakest part of my argument since it's pure speculation, I think that the Monoid-based instance is more useful in practice. For some loosely analogous historical precedent, I would point to the Monoid instance for IO. In July 2014, Edward Kmett pointed out that one issue with admitting a Monoid instance for IO was that two possible instances existed (<a href="https://mail.haskell.org/pipermail/glasgow-haskell-users/2014-July/025124.html">https://mail.haskell.org/pipermail/glasgow-haskell-users/2014-July/025124.html</a>). One of them monoidally concatenated the results, and the one was identical to IO's Alternative instance. Sounds familiar. Ultimately though, in November 2014, when Gabriel Gonzalez proposed the instance that monoidally concatenated results, nobody objected (<a href="https://mail.haskell.org/pipermail/libraries/2014-November/024310.html">https://mail.haskell.org/pipermail/libraries/2014-November/024310.html</a>). Every person who responded on the mailing list wanted the Monoid-based instance, not the one that was a copy of Alternative. (And it's always recoverable via Alt in the event that someone does want it). So, and again I want to stress that I consider this a weaker part of the argument since it's more rooted in human preferences rather than logic, I think the Monoid-based instance is just generally more useful in practice. In my own experience, I often have wanted the Monoid-based instances, but I have never wanted the Alternative-based instances</div><div><br></div><div>With all that said, I would like to ask that the Monoid-based Monoid instances for ReaderT, WriterT, StateT, AccumT, etc. be considered. I would also appreciate any feedback or further discussion of this issue.</div><div><br><div><div>-- <br><div class="gmail_signature">-Andrew Thaddeus Martin</div>
</div></div></div></div></div>