Monoid instance for IO

I would like to add the following `Monoid` instance for `IO` to `Data.Monoid`: ``` instance Monoid a => Monoid (IO a) where mempty = pure mempty mappend = liftA2 mappend ``` I describe the benefit of this particular instance in this blog post: http://www.haskellforall.com/2014/07/equational-reasoning-at-scale.html ... and Conal Elliot describes the general trick of recursively lifting `Monoid` instances in his type class morphisms paper: http://conal.net/papers/type-class-morphisms/type-class-morphisms-long.pdf The primary benefit of the `Monoid` instance is that it chains well with other `Monoid` instances in `base` to create derived `Monoid` instances. The following types are examples of useful derived `Monoid` instances: ``` IO () -- Because `()` is a `Monoid` a -> IO () -- Because `a -> r` is a `Monoid` if `r` is a `Monoid` IO (a -> IO ()) -- This comment explains the utility of this instance: http://www.reddit.com/r/haskell/comments/22bn1m/monads_lifting_join_and_side... ``` Here are other alternatives that I considered: **Alternative A)** Define a newtype for the `Monoid` instance, either specialized to `IO`: ``` newtype IOMonoid a = IOMonoid { getIOMonoid :: IO a } deriving (Functor, Applicative, Monad) instance Monoid a => Monoid (IOMonoid a) where mempty = pure mempty mappend = liftA2 mappend ``` ... or generalized to all applicatives: ``` newtype LiftMonoid f a = LiftMonoid ( getLiftMonoid :: f a } instance (Applicative f, Monoid a) => Monoid (LiftMonoid f a) where ... ``` I prefer not to use a newtype because the principle benefit of a `Monoid` instance for `IO` is for the derived instances. Using the example `IO (a -> IO ())` type, suppose that I had two values of that type which I wanted to mappend: ``` m :: IO (a -> IO ()) n :: IO (a -> IO ()) ``` Using newtypes (either one), I'd have to write: ``` getNewtype (Newtype (fmap (fmap Newtype) m) <> Newtype (fmap (fmap Newtype) n)) ``` ... instead of just: ``` m <> n ``` **Alternative B)** Provide a different `Monoid` instance for `IO`, such as one that uses concurrency There are two issues with this approach: 1. There is not a well-defined semantics for non-`STM` concurrency that we could use to prove the `Monoid` laws 2. Even if there were a well-defined semantics, it would be better suited as an `Alternative` instance instead of a `Monoid` instance To clarify the latter point, Peaker convinced me [[ http://www.reddit.com/r/haskell/comments/2guo44/what_is_wrong_with_the_monoi... | here ]] that for certain `Applicative`s it's worth distinguishing the behavior of the `Alternative` instance from the behavior of the `Monoid` instance. The `Monoid` instance can recursively delegate to the `Monoid` instance of the `Applicative`'s type parameter, whereas the `Alternative` instance cannot. I also created a task on phabricator here since I'm used to the Github style of discussing issues on the repository issue tracker: https://phabricator.haskell.org/T55?workflow=create

+1 I use a newtype for this, and it's a nuisance. Anthony
On Nov 13, 2014, at 10:13 AM, Gabriel Gonzalez
wrote: I would like to add the following `Monoid` instance for `IO` to `Data.Monoid`:
``` instance Monoid a => Monoid (IO a) where mempty = pure mempty mappend = liftA2 mappend ```
I describe the benefit of this particular instance in this blog post:
http://www.haskellforall.com/2014/07/equational-reasoning-at-scale.html
... and Conal Elliot describes the general trick of recursively lifting `Monoid` instances in his type class morphisms paper:
http://conal.net/papers/type-class-morphisms/type-class-morphisms-long.pdf
The primary benefit of the `Monoid` instance is that it chains well with other `Monoid` instances in `base` to create derived `Monoid` instances. The following types are examples of useful derived `Monoid` instances:
``` IO () -- Because `()` is a `Monoid`
a -> IO () -- Because `a -> r` is a `Monoid` if `r` is a `Monoid`
IO (a -> IO ()) -- This comment explains the utility of this instance: http://www.reddit.com/r/haskell/comments/22bn1m/monads_lifting_join_and_side... ```
Here are other alternatives that I considered:
**Alternative A)** Define a newtype for the `Monoid` instance, either specialized to `IO`:
``` newtype IOMonoid a = IOMonoid { getIOMonoid :: IO a } deriving (Functor, Applicative, Monad)
instance Monoid a => Monoid (IOMonoid a) where mempty = pure mempty mappend = liftA2 mappend ```
... or generalized to all applicatives:
``` newtype LiftMonoid f a = LiftMonoid ( getLiftMonoid :: f a }
instance (Applicative f, Monoid a) => Monoid (LiftMonoid f a) where ... ```
I prefer not to use a newtype because the principle benefit of a `Monoid` instance for `IO` is for the derived instances. Using the example `IO (a -> IO ())` type, suppose that I had two values of that type which I wanted to mappend:
``` m :: IO (a -> IO ()) n :: IO (a -> IO ()) ```
Using newtypes (either one), I'd have to write:
``` getNewtype (Newtype (fmap (fmap Newtype) m) <> Newtype (fmap (fmap Newtype) n)) ```
... instead of just:
``` m <> n ```
**Alternative B)** Provide a different `Monoid` instance for `IO`, such as one that uses concurrency
There are two issues with this approach:
1. There is not a well-defined semantics for non-`STM` concurrency that we could use to prove the `Monoid` laws 2. Even if there were a well-defined semantics, it would be better suited as an `Alternative` instance instead of a `Monoid` instance
To clarify the latter point, Peaker convinced me [[ http://www.reddit.com/r/haskell/comments/2guo44/what_is_wrong_with_the_monoi... | here ]] that for certain `Applicative`s it's worth distinguishing the behavior of the `Alternative` instance from the behavior of the `Monoid` instance. The `Monoid` instance can recursively delegate to the `Monoid` instance of the `Applicative`'s type parameter, whereas the `Alternative` instance cannot.
I also created a task on phabricator here since I'm used to the Github style of discussing issues on the repository issue tracker:
https://phabricator.haskell.org/T55?workflow=create _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

On Thu Nov 13 2014 at 5:13:21 PM Gabriel Gonzalez
I would like to add the following `Monoid` instance for `IO` to `Data.Monoid`:
``` instance Monoid a => Monoid (IO a) where mempty = pure mempty mappend = liftA2 mappend ```
I describe the benefit of this particular instance in this blog post:
http://www.haskellforall.com/2014/07/equational-reasoning-at-scale.html
... and Conal Elliot describes the general trick of recursively lifting `Monoid` instances in his type class morphisms paper:
http://conal.net/papers/type-class-morphisms/type-class-morphisms-long.pdf
The primary benefit of the `Monoid` instance is that it chains well with other `Monoid` instances in `base` to create derived `Monoid` instances. The following types are examples of useful derived `Monoid` instances:
``` IO () -- Because `()` is a `Monoid`
a -> IO () -- Because `a -> r` is a `Monoid` if `r` is a `Monoid`
IO (a -> IO ()) -- This comment explains the utility of this instance: http://www.reddit.com/r/haskell/comments/22bn1m/monads_lifting_join_and_ sideeffecting_actions/cglhgu0 ```
Here are other alternatives that I considered:
**Alternative A)** Define a newtype for the `Monoid` instance, either specialized to `IO`:
``` newtype IOMonoid a = IOMonoid { getIOMonoid :: IO a } deriving (Functor, Applicative, Monad)
instance Monoid a => Monoid (IOMonoid a) where mempty = pure mempty mappend = liftA2 mappend ```
... or generalized to all applicatives:
``` newtype LiftMonoid f a = LiftMonoid ( getLiftMonoid :: f a }
instance (Applicative f, Monoid a) => Monoid (LiftMonoid f a) where ... ```
I prefer not to use a newtype because the principle benefit of a `Monoid` instance for `IO` is for the derived instances. Using the example `IO (a -> IO ())` type, suppose that I had two values of that type which I wanted to mappend:
``` m :: IO (a -> IO ()) n :: IO (a -> IO ()) ```
Using newtypes (either one), I'd have to write:
``` getNewtype (Newtype (fmap (fmap Newtype) m) <> Newtype (fmap (fmap Newtype) n)) ```
... instead of just:
``` m <> n ```
**Alternative B)** Provide a different `Monoid` instance for `IO`, such as one that uses concurrency
There are two issues with this approach:
1. There is not a well-defined semantics for non-`STM` concurrency that we could use to prove the `Monoid` laws 2. Even if there were a well-defined semantics, it would be better suited as an `Alternative` instance instead of a `Monoid` instance
To clarify the latter point, Peaker convinced me [[ http://www.reddit.com/r/haskell/comments/2guo44/what_ is_wrong_with_the_monoid_instance_for_maybe/ckmrcux | here ]] that for certain `Applicative`s it's worth distinguishing the behavior of the `Alternative` instance from the behavior of the `Monoid` instance. The `Monoid` instance can recursively delegate to the `Monoid` instance of the `Applicative`'s type parameter, whereas the `Alternative` instance cannot.
I also created a task on phabricator here since I'm used to the Github style of discussing issues on the repository issue tracker:
https://phabricator.haskell.org/T55?workflow=create _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
This seems reasonable to me. What are the downsides? The only one I can really see is breaking existing orphan instances, which is not something I'd be concerned about. +1 from me.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 I've been waiting for this for a while. +1. At least until someone presents downsides that I've been overlooking. - -- Alexander alexander@plaimi.net https://secure.plaimi.net/~alexander -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/ iF4EAREIAAYFAlRkzvoACgkQRtClrXBQc7VDVgEAlswOK3dOOaooYtbh48yoWokh qDHdjy6ryYDvUMmCp/0A/0d5xHR9cUFQ9jcWLE9KXDKZ6oJvOxVDGMz2yMcAp+uo =zP7M -----END PGP SIGNATURE-----

Very +1
Just yesterday I was hoping to have a (Monad m, Monoid w) => Monoid (m w)
instance; that's probably not possible because it conflicts with too many
things, such as the Monoid w => Monoid (Maybe w) instance, but having
Monoid w => Monoid (IO w) would have solved my problem. Specifically, this
was really useful for me with Foldable, because it allows foldMap to work
in a monad; I've had to define my own `foldMapM` several times already.
-- Andrew
On Thu, Nov 13, 2014 at 7:32 AM, Alexander Berntsen
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256
I've been waiting for this for a while.
+1. At least until someone presents downsides that I've been overlooking.
- -- Alexander alexander@plaimi.net https://secure.plaimi.net/~alexander -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
iF4EAREIAAYFAlRkzvoACgkQRtClrXBQc7VDVgEAlswOK3dOOaooYtbh48yoWokh qDHdjy6ryYDvUMmCp/0A/0d5xHR9cUFQ9jcWLE9KXDKZ6oJvOxVDGMz2yMcAp+uo =zP7M -----END PGP SIGNATURE----- _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

On 2014-11-13 at 16:13:09 +0100, Gabriel Gonzalez wrote:
I would like to add the following `Monoid` instance for `IO` to Data.Monoid`:
``` instance Monoid a => Monoid (IO a) where mempty = pure mempty mappend = liftA2 mappend ```
seems reasonable to me, +1 from me Cheers, hvr PS: There was a related discussion earlier this year at http://thread.gmane.org/gmane.comp.lang.haskell.glasgow.user/24835/focus=248... mentioning "instance Monoid a => Monoid (IO a)" already

+1 from me.
On Thu, Nov 13, 2014 at 10:13 AM, Gabriel Gonzalez
I would like to add the following `Monoid` instance for `IO` to `Data.Monoid`:
``` instance Monoid a => Monoid (IO a) where mempty = pure mempty mappend = liftA2 mappend ```
I describe the benefit of this particular instance in this blog post:
http://www.haskellforall.com/2014/07/equational-reasoning-at-scale.html
... and Conal Elliot describes the general trick of recursively lifting `Monoid` instances in his type class morphisms paper:
http://conal.net/papers/type-class-morphisms/type-class-morphisms-long.pdf
The primary benefit of the `Monoid` instance is that it chains well with other `Monoid` instances in `base` to create derived `Monoid` instances. The following types are examples of useful derived `Monoid` instances:
``` IO () -- Because `()` is a `Monoid`
a -> IO () -- Because `a -> r` is a `Monoid` if `r` is a `Monoid`
IO (a -> IO ()) -- This comment explains the utility of this instance: http://www.reddit.com/r/haskell/comments/22bn1m/monads_lifting_join_and_ sideeffecting_actions/cglhgu0 ```
Here are other alternatives that I considered:
**Alternative A)** Define a newtype for the `Monoid` instance, either specialized to `IO`:
``` newtype IOMonoid a = IOMonoid { getIOMonoid :: IO a } deriving (Functor, Applicative, Monad)
instance Monoid a => Monoid (IOMonoid a) where mempty = pure mempty mappend = liftA2 mappend ```
... or generalized to all applicatives:
``` newtype LiftMonoid f a = LiftMonoid ( getLiftMonoid :: f a }
instance (Applicative f, Monoid a) => Monoid (LiftMonoid f a) where ... ```
I prefer not to use a newtype because the principle benefit of a `Monoid` instance for `IO` is for the derived instances. Using the example `IO (a -> IO ())` type, suppose that I had two values of that type which I wanted to mappend:
``` m :: IO (a -> IO ()) n :: IO (a -> IO ()) ```
Using newtypes (either one), I'd have to write:
``` getNewtype (Newtype (fmap (fmap Newtype) m) <> Newtype (fmap (fmap Newtype) n)) ```
... instead of just:
``` m <> n ```
**Alternative B)** Provide a different `Monoid` instance for `IO`, such as one that uses concurrency
There are two issues with this approach:
1. There is not a well-defined semantics for non-`STM` concurrency that we could use to prove the `Monoid` laws 2. Even if there were a well-defined semantics, it would be better suited as an `Alternative` instance instead of a `Monoid` instance
To clarify the latter point, Peaker convinced me [[ http://www.reddit.com/r/haskell/comments/2guo44/what_ is_wrong_with_the_monoid_instance_for_maybe/ckmrcux | here ]] that for certain `Applicative`s it's worth distinguishing the behavior of the `Alternative` instance from the behavior of the `Monoid` instance. The `Monoid` instance can recursively delegate to the `Monoid` instance of the `Applicative`'s type parameter, whereas the `Alternative` instance cannot.
I also created a task on phabricator here since I'm used to the Github style of discussing issues on the repository issue tracker:
https://phabricator.haskell.org/T55?workflow=create _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
participants (7)
-
Alexander Berntsen
-
Andrew Gibiansky
-
Anthony Cowley
-
Edward Kmett
-
Gabriel Gonzalez
-
Herbert Valerio Riedel
-
Michael Snoyman