Why no `instance (Monoid a, Applicative f)=> Monoid (f a)` for IO?

Brandon Simmons brandon.m.simmons at gmail.com
Tue Jul 15 14:31:59 UTC 2014

On Mon, Jul 14, 2014 at 10:55 PM, Edward Kmett <ekmett at gmail.com> wrote:
> There are monads for which you want another Monoid, e.g. Maybe provides a
> different unit, because it pretends to lift a Semigroup into a Monoid.
> There are also monoids that take a parameter of kind * that would overlap
> with this instance.
> So we can't (and shouldn't) have the global Monoid instance like you give
> there first.

Right, sorry. I just meant that as a bit of context. My proposal is adding
`instance Monoid a => Monoid (IO a)`.

> As for the particular case of IO a, lifting may be a reasonable option
> there.
> A case could be made for adding an `instance Monoid a => Monoid (IO a)`, but
> for such a ubiquitously used type, expect that this wouldn't be an easy
> sell.
> You'd possibly have to deal with everyone and their brother coming out of
> the woodwork offering up every other Monoid they happened to use on IO.
> Why?
> IO provides a notion of failing action you could use for zero and you can
> build an (<|>) like construction on it as well, so the 'multiplicative'
> structure isn't the _only_ option for your monoid.

Can you give an example of what you mean here? Would that be something
involving exceptions?

> Even within the multiplicative structure using the monoid isn't necessarily
> ideal as you might leak more memory with an IO a monoid that lifts () than
> you would with working specifically on IO ().
> You can argue the case that the choice you made is a sensible default
> instance by instance, but when there isn't a real canonical choice we do
> tend to err on the side of leaving things open as orphans are at least
> possible, but once the choice is made it is very very hard to unmake.

Right like Sum/Product for Num types. But here there's good reason, I
think, to choose one instance over others, because we already have the
monoid structure of Applicative and Monad. You can still have a
wrapper newtype with different instances for the alternatives, as was
done with Applicative for [] and ZipList.

But I might be misunderstanding, since I'm not really sure what the
alternative instances you mention would look like.


> I say this mostly so you know the kinds of objections proposals like this
> usually see, not to flat out reject the idea of the particular case of this
> instance for IO.
> I will say the global 'instance (Applicative f, Monoid m) => Monoid (f m)'
> won't fly for overlap reasons though.
> -Edward
> On Mon, Jul 14, 2014 at 6:55 PM, Brandon Simmons
> <brandon.m.simmons at gmail.com> wrote:
>> It seems to me that this should be true for all `f a` like:
>>   instance (Monoid a, Applicative f)=> Monoid (f a) where
>>       mappend = liftA2 mappend
>>       mempty = pure mempty
>> But I can't seem to find the particular `instance (Monoid a)=> Monoid
>> (IO a)` anywhere. Would that instance be incorrect, or does it live
>> somewhere else?
>> FWIW I noticed this when I started thinking about an instance I wanted
>> for 'contravariant':
>>   instance (Monoid a, Applicative f)=> Monoid (Op (f a) b) where
>>       mempty = Op $ const $ pure mempty
>>       mappend (Op f) (Op g) = Op (\b-> liftA2 mappend (f b) (g b))
>> at which point I realized (I think) all `f a` are monoidal, and so we
>> ought to be able to get the instance above with just a deriving
>> Monoid.
>> Brandon
>> _______________________________________________
>> Glasgow-haskell-users mailing list
>> Glasgow-haskell-users at haskell.org
>> http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

More information about the Glasgow-haskell-users mailing list