suggestions for improving MonadWriter

Iavor Diatchki iavor.diatchki at gmail.com
Tue Jan 29 08:31:58 CET 2013


Hi,

just for the record, this split is already made in `MonadLib` (e.g.,
monadLib's `collect` method, which corresponds to what Petr called
`contained`, is in its own class called `RunWriterM`;  there are similar
classes for readers and exceptions).

-Iavor


On Mon, Jan 28, 2013 at 9:49 AM, Edward Kmett <ekmett at gmail.com> wrote:

> To be clear, I'm not yet making a concrete proposal for breaking up
> MonadReader and MonadWriter. Doing so would be fairly painful for at least
> some users in the short term. There are a lot of custom MonadReader and
> MonadWriter instances out there, and it also affects the signature of all
> combinators that consume these types that use pass, local or listen. That
> said the latter is probably only about 10% of the code that uses ask/tell.
> Moreso in the reader case than the writer case.
>
> Given the number of users who have to support multiple platform releases,
> a concrete proposal would have to address the upgrade path, possibly
> including making MonadLocal and MonadListen stub subclasses a version or so
> early and then moving methods over later, etc. so users can upgrade
> appropriately, and library authors can follow suit, permitting both an
> upgrade path or deciding to rip the bandaid off in one go. The logistics of
> this are somewhat convoluted, and if we're going to go through that pain,
> it may be worth addressing other concerns at the same time.
>
> -Edward
>
> On Jan 28, 2013, at 12:10 PM, Petr P <petr.mvd at gmail.com> wrote:
>
> Edward,
>
> 2013/1/28 Edward A Kmett <ekmett at gmail.com>
>
>> I've often wanted to remove the Monoid constraint, so I'm a strong +1 on
>> the first one.
>>
>> The second one I'm -1 on, given that it can be expressed with the
>> existing combinators and due in small part to a more ideological concern:
>>
>> Over time I've come to view cramming pass/listen and local into the
>> respective MonadWriter and MonadReader classes as a mistake. If we had the
>> hierarchy to do over, I'd probably want them split into separate
>> subclasses. This would permit more instances involving Cont, logging to
>> disk, etc.
>>
>
> I strongly support your idea. Currently, MonadWriter and MonadReader just
> reflect ReaderT and WriterT so that we can make monad stacks. But they
> don't reflect the ideas - a monad we write to or read from, without any
> additional constraints. I'd really love to have them split up: A more
> general class that has just 'tell' and 'writer', and another that also has
> 'listen' and 'pass' (perhaps also 'contained').
> I tried to discuss it in haskell-cafe once <http://www.mail-archive.com/
> haskell-cafe at haskell.org/msg101756.html>, but without any feedback on the
> idea, so I thought everybody is happy with the current state.
>
> In this case, it seems that nothing important would break by this change,
> because IMHO there are very very little custom instances of
> MonadWriter/Reader. I'm also a big fan of this proposal: http://hackage.
> haskell.org/trac/ghc/wiki/DefaultSuperclassInstances
> If it were implemented and standardized, we could refactor type classes
> without breaking existing code (or breaking it just very little).
> [In particular, it'd be possible to make Monad a subclass of Applicative,
> something I've always regretted we don't have.]
>
> Other than that, I'm not sure how adding "contained" goes against this
> idea. If we split MonadWriter into two type classes, "contained" would
> simply go into the one with "listen" and "pass".
>
> Best regards,
> Petr
>
>
>
>>
>> Also, the former can be done purely within the mtl, while the latter
>> drags transformers into it.
>>
>
>
>>
>> -Edward
>>
>> On Jan 27, 2013, at 3:30 PM, Petr P <petr.mvd at gmail.com> wrote:
>>
>>   Dear maintainers,
>>
>> I have two suggestions for MonadWriter:
>>
>> (1) Remove the "Monoid w" constraint from the definition.
>>
>> The constraints prevent creating new instances of the class that have
>> only an implied monoid. For example, I needed to create a simple writer
>> which always stores the last written element. I had to wrap it into Last,
>> which was a nuisance for users of my library. Without the constraint, my
>> instance would be quite simpler and still satisfying all the laws. There
>> are many other similar use cases, like counting the number of written
>> values (and disregarding their actual content) etc.
>>
>> The constraint is meant to ensure that instances of that class obey the
>> monad laws. But it's not the responsibility of a type class that its
>> instances satisfy the laws. They could violate them even without this
>> constraints. Instead, this constraint should be specified (and it is) in
>> the definition of their instances.
>>
>> It has been discussed in haskell-cafe <http://www.haskell.org/pipermail/
>> haskell-cafe/2012-December/thread.html#105088> with arguments for and
>> against.
>>
>>
>> (2) Add
>>
>>   -- | @contained m@ executes the action @m@ in a contained environment
>> and
>>   -- returns its value and its output. The current output is not
>> modified.
>>   contained :: m a -> m (a, w)
>>
>> to MonadWriter.
>>
>> This generalizes "pass" and "listen"  and has it's a sort of inverse to
>> "writer" with these simple laws:
>>
>>   writer <=< contained   = id
>>   contained . writer     = return
>>
>> It seems as a understandable set of laws that its instances should obey.
>>
>> It also expresses the same concept as "runWriterT" does, but inside the
>> type class. In particular, for "WriterT" we have
>>
>>   contained :: (Monoid w, Monad m) => WriterT w m a -> WriterT w m (a, w)
>>   contained = lift . runWriterT
>>
>> Current instances won't be affected as "contained" can be expressed using
>> "pass" and "listen" (and vice versa).
>> Full details available at
>>
>> https://github.com/ppetr/mtl/compare/9cc075cfaa40028a54f1dedf62af67e912f9fd42...master
>> [There "contained" is expressed without the "Monoid w" constraint as
>> suggested in (1). If we keep the constraint, "contained" can be expressed
>> more simply as
>>   containde k = pass (listen k >>= \x -> return (x, const mempty)).
>>
>> Also, "contained" isn't probably a good name, I just couldn't think of
>> anything better.]
>>
>>
>>    Best regards,
>>    Petr Pudlak
>>
>> _______________________________________________
>> Libraries mailing list
>> Libraries at haskell.org
>> http://www.haskell.org/mailman/listinfo/libraries
>>
>>
>
> _______________________________________________
> Libraries mailing list
> Libraries at haskell.org
> http://www.haskell.org/mailman/listinfo/libraries
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/libraries/attachments/20130128/65b4c061/attachment-0001.htm>


More information about the Libraries mailing list