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

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

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. 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. 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. 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@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@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users

On Mon, Jul 14, 2014 at 10:55 PM, Edward Kmett
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. Thanks, Brandon
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
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@haskell.org http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
participants (2)
-
Brandon Simmons
-
Edward Kmett