proposal #4095: add Applicative instance for Either

The proposal is to add this instance to Control.Applicative: instance Applicative (Either e) where pure = Right Left e <*> _ = Left e Right f <*> r = fmap f r This is not the only possible instance for Either, but this one is compatible with the usual Monad instance. Deadline: 15th June 2010

On Tue, 25 May 2010, Ross Paterson wrote:
The proposal is to add this instance to Control.Applicative:
instance Applicative (Either e) where pure = Right Left e <*> _ = Left e Right f <*> r = fmap f r
This is not the only possible instance for Either, but this one is compatible with the usual Monad instance.
What other sensible instances are there? Nevertheless I think, since the Monad instance for Either exists, there is no choice other than make the Applicative instance compatible to it and it is better than no instance at all, in order to prevent orphan instances in user modules.

On Tue, May 25, 2010 at 03:15:57PM +0200, Henning Thielemann wrote:
On Tue, 25 May 2010, Ross Paterson wrote:
The proposal is to add this instance to Control.Applicative:
instance Applicative (Either e) where pure = Right Left e <*> _ = Left e Right f <*> r = fmap f r
This is not the only possible instance for Either, but this one is compatible with the usual Monad instance.
What other sensible instances are there?
The "all errors" instance: instance Monoid e => Applicative (Either e) where pure = Right Left e1 <*> Left e2 = Left (e1 `mappend` e2) Left e1 <*> Right _ = Left e1 Right _ <*> Left e2 = Left e2 Right f <*> Right x = Right (f x)

+1
On Tue, May 25, 2010 at 8:41 AM, Ross Paterson
The proposal is to add this instance to Control.Applicative:
instance Applicative (Either e) where pure = Right Left e <*> _ = Left e Right f <*> r = fmap f r
This is not the only possible instance for Either, but this one is compatible with the usual Monad instance.
Deadline: 15th June 2010 _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries

Ross Paterson wrote:
The proposal is to add this instance to Control.Applicative: instance Applicative (Either e) where... compatible with the usual Monad instance.
+1 for this Applicative instance -1 for the orphan Monad instance with the silly superclass constraint that this Applicative instance is meant to extend

+1 for this Applicative instance.
I assume that when this instance is added to Control.Applicative, the
one in Control.Monad.Trans.Error from transformers will be removed.
On Tue, May 25, 2010 at 6:22 PM, Yitzchak Gale
-1 for the orphan Monad instance with the silly superclass constraint that this Applicative instance is meant to extend
I assume you are referring to: instance (Error e) => Monad (Either e) where return = Right Left l >>= _ = Left l Right r >>= k = k r fail msg = Left (strMsg msg) defined in Control.Monad.Trans.Error from transformers or Control.Monad.Error from mtl. Thinking out loudly: since the 'fail' method is considered to be a mistake, what about not defining it? As in: instance Monad (Either e) where return = Right Left l >>= _ = Left l Right r >>= k = k r Regards, Bas

On Tue, 25 May 2010, Bas van Dijk wrote:
+1 for this Applicative instance.
I assume that when this instance is added to Control.Applicative, the one in Control.Monad.Trans.Error from transformers will be removed.
On Tue, May 25, 2010 at 6:22 PM, Yitzchak Gale
wrote: -1 for the orphan Monad instance with the silly superclass constraint that this Applicative instance is meant to extend
I assume you are referring to:
instance (Error e) => Monad (Either e) where return = Right Left l >>= _ = Left l Right r >>= k = k r fail msg = Left (strMsg msg)
defined in Control.Monad.Trans.Error from transformers or Control.Monad.Error from mtl.
I think the problem with this instance is that it is orphan. You get a conflict if you import both mtl and transformers. But I assume that it is not possible to add this instance to Prelude, since this may break Haskell 98 programs. Maybe MTL could import that instance from Transformers in order to avoid duplicate definition.

I wrote:
-1 for the orphan Monad instance with the silly superclass constraint that this Applicative instance is meant to extend
Henning Thielemann wrote:
I assume you are referring to... defined in Control.Monad.Trans.Error from transformers or Control.Monad.Error from mtl.
Indeed.
I think the problem with this instance is that it is orphan.
That's one problem - and a serious one. The other problem is the superclass restriction. That only makes sense in the context of mtl or transformers. But the Either monad is far more generally useful than that. mtl should have defined a its own type instead of using up the Monad slot for the Prelude Either type. Regards, Yitz

Either's Monad instance (whatever we decide the canonical instance ought to be*) should be in Control.Monad.Instances just like Either's Functor instance is. * how many people use the current "(Error e) => Monad (Either e)" instance who would break if we change it? If we don't like the Error class, then what do we replace it with? (Monad Maybe ignores the message -- but Monad (Either e) has to generate an "e", unless we let fail=error which may be an acceptable decision after all. If we have a class to generate an "e", we could use that to make a MonadPlus instance too (mzero)... but even then, the Monad instance perhaps shouldn't depend on that class...) Assuming we do want to keep Either having a Monad instance at all, we do as Ross says have to pick the "leftmost error" rather than "all errors" Applicative instance. (Monad cannot produce errors beyond the leftmost, due to its later-actions-depend-on-results-from-earlier-ones nature.) -Isaac

Isaac Dupree schrieb:
Either's Monad instance (whatever we decide the canonical instance ought to be*) should be in Control.Monad.Instances just like Either's Functor instance is.
* how many people use the current "(Error e) => Monad (Either e)" instance who would break if we change it? If we don't like the Error class, then what do we replace it with? (Monad Maybe ignores the message -- but Monad (Either e) has to generate an "e", unless we let fail=error which may be an acceptable decision after all. If we have a class to generate an "e", we could use that to make a MonadPlus instance too (mzero)... but even then, the Monad instance perhaps shouldn't depend on that class...)
Assuming we do want to keep Either having a Monad instance at all, we do as Ross says have to pick the "leftmost error" rather than "all errors" Applicative instance. (Monad cannot produce errors beyond the leftmost, due to its later-actions-depend-on-results-from-earlier-ones nature.)
I can live without the Monad Either instance.

On Tue, May 25, 2010 at 2:32 PM, Henning Thielemann < schlepptop@henning-thielemann.de> wrote:
Isaac Dupree schrieb:
Assuming we do want to keep Either having a Monad instance at all, we do as Ross says have to pick the "leftmost error" rather than "all errors" Applicative instance. (Monad cannot produce errors beyond the leftmost, due to its later-actions-depend-on-results-from-earlier-ones nature.)
I can live without the Monad Either instance.
I too would be much happier if the current Monad Either instance were to vanish from mtl/transformers/monads-(fd/tf) as the annoying Error constraint breaks a number of wonderful theoretical properties. In my perfect world the next version of transformers would provide an entry in Control.Monad.Instances for: instance Applicative (Either m) instance Alternative (Either m) instance Monad (Either m) instance MonadPlus (Either m) of which the instance for Applicative could be pushed up to Control.Applicative as part of this proposal, with Alternative making the move later and then Control.Monad.Error could follow suit with the way State, Reader and Writer are being redefined as type aliases of their transformers wrapped around identity, ditching the Either instance in favor of ErrorT e Identity for symmetry. Sadly, the class used by Control.Monad.Error is named Error, robbing us of the obvious name for the type alias. That said it would break a fair amount of existing code, and is a separate issue pertaining to a separate library and so should probably be considered on its own merits in another proposal as anything that relied on importing Control.Monad.Error and being able to then do [ x | Right x <- xs ] would break. But there are quite possibly too many moving parts to make such a big jump, and then you have the other bikeshedding issue of there being yet another definition for Either that makes sense to default to: instance Monoid m => Applicative (Either m) instance Monoid m => Alternative (Either m) instance Monoid m => Monad (Either m) instance Monoid m => MonadPlus (Either m) which would likely ensure that any such concrete proposal wings up stillborn. The former monad is nice for things like implementing generalized apomorphisms as generalized anamorphisms, for symmetry with implementing zygomorphisms (generalized paramorphisms) as generalized catamorphisms, the latter has less theoretical appeal but it is still easy to remember, convenient to use, and has a pseudo-symmetric relationship to Monoid m => Monad ((,) m). -Edward Kmett

On Tue, May 25, 2010 at 04:55:09PM -0400, Edward Kmett wrote:
In my perfect world the next version of transformers would provide an entry in Control.Monad.Instances for:
instance Applicative (Either m) instance Alternative (Either m) instance Monad (Either m) instance MonadPlus (Either m)
What definition of empty/mzero do you have in mind?
But there are quite possibly too many moving parts to make such a big jump, and then you have the other bikeshedding issue of there being yet another definition for Either that makes sense to default to:
instance Monoid m => Applicative (Either m) instance Monoid m => Alternative (Either m) instance Monoid m => Monad (Either m) instance Monoid m => MonadPlus (Either m)
I can't think of a Monad instance that uses the Monoid constraint. What is the instance you mean?

On Tue, May 25, 2010 at 6:34 PM, Ross Paterson
On Tue, May 25, 2010 at 04:55:09PM -0400, Edward Kmett wrote:
In my perfect world the next version of transformers would provide an entry in Control.Monad.Instances for:
instance Applicative (Either m) instance Alternative (Either m) instance Monad (Either m) instance MonadPlus (Either m)
What definition of empty/mzero do you have in mind?
Thats what I get for dashing off my response half baked. =) Upon actually looking, the code that I have for that Applicative didn't supply an Alternative/MonadPlus instance, since there isn't a suitable 'Default' class that provides a single value without a full monoid, and if it existed, you'd want to use it to support 'fail'
But there are quite possibly too many moving parts to make such a big jump, and then you have the other bikeshedding issue of there being yet another definition for Either that makes sense to default to:
instance Monoid m => Applicative (Either m) instance Monoid m => Alternative (Either m) instance Monoid m => Monad (Either m) instance Monoid m => MonadPlus (Either m)
I can't think of a Monad instance that uses the Monoid constraint. What is the instance you mean?
The Monoid instance only exists to provide mempty to support fail, in a manner consistent with the Alternative and MonadPlus instance as no weaker class exists to use to obtain a Default. Neither answer is really entirely satisfying. -Edward Kmett

On Tue, May 25, 2010 at 07:08:26PM +0200, Bas van Dijk wrote:
I assume that when this instance is added to Control.Applicative, the one in Control.Monad.Trans.Error from transformers will be removed.
Yes. It may need some ifdeffery.
On Tue, May 25, 2010 at 6:22 PM, Yitzchak Gale
wrote: -1 for the orphan Monad instance with the silly superclass constraint that this Applicative instance is meant to extend
I assume you are referring to:
instance (Error e) => Monad (Either e) where return = Right Left l >>= _ = Left l Right r >>= k = k r fail msg = Left (strMsg msg)
defined in Control.Monad.Trans.Error from transformers or Control.Monad.Error from mtl.
Thinking out loudly: since the 'fail' method is considered to be a mistake, what about not defining it? As in:
instance Monad (Either e) where return = Right Left l >>= _ = Left l Right r >>= k = k r
Fine by me. Making it a non-orphan would break Haskell 98 compatibility, though. You'd also have to delete its MonadPlus instance.

On Tue, May 25, 2010 at 7:21 PM, Ross Paterson
Fine by me. Making it a non-orphan would break Haskell 98 compatibility, though.
Making it non-orphan means moving the Monad Either instance to either GHC.Base (which defines the Monad class) or to Data.Either (which defines the Either type). How exactly does this break Haskell 98 compatibility? Regards, Bas

On Tue, 25 May 2010, Bas van Dijk wrote:
On Tue, May 25, 2010 at 7:21 PM, Ross Paterson
wrote: Fine by me. Making it a non-orphan would break Haskell 98 compatibility, though.
Making it non-orphan means moving the Monad Either instance to either GHC.Base (which defines the Monad class) or to Data.Either (which defines the Either type). How exactly does this break Haskell 98 compatibility?
Since there is no Monad Either instance in Haskell 98, Haskell 98 programs can safely define their own one. If we would add such an instance to the base library this would break those programs.

On Tue, May 25, 2010 at 7:59 PM, Henning Thielemann
On Tue, 25 May 2010, Bas van Dijk wrote:
On Tue, May 25, 2010 at 7:21 PM, Ross Paterson
wrote: Fine by me. Making it a non-orphan would break Haskell 98 compatibility, though.
Making it non-orphan means moving the Monad Either instance to either GHC.Base (which defines the Monad class) or to Data.Either (which defines the Either type). How exactly does this break Haskell 98 compatibility?
Since there is no Monad Either instance in Haskell 98, Haskell 98 programs can safely define their own one. If we would add such an instance to the base library this would break those programs.
Oh yes I see. Bummer! Bas

On Tue, May 25, 2010 at 07:55:42PM +0200, Bas van Dijk wrote:
On Tue, May 25, 2010 at 7:21 PM, Ross Paterson
wrote: Fine by me. Making it a non-orphan would break Haskell 98 compatibility, though.
Making it non-orphan means moving the Monad Either instance to either GHC.Base (which defines the Monad class) or to Data.Either (which defines the Either type). How exactly does this break Haskell 98 compatibility?
This Haskell 98 module will not be accepted: module M where instance Monad (Either a)

On 2010-05-25 05:41, Ross Paterson wrote:
The proposal is to add this instance to Control.Applicative:
instance Applicative (Either e) where pure = Right Left e<*> _ = Left e Right f<*> r = fmap f r
This is not the only possible instance for Either, but this one is compatible with the usual Monad instance.
Deadline: 15th June 2010
I currently have my own implementations for these: instance Foldable Identity instance Traversable Identity instance Applicative Identity instance Foldable (Either p) instance Traversable (Either p) instance Applicative (Either p) instance Foldable ((,) p) instance Traversable ((,) p) Do any of these others already exist? -- Ashley Yakeley
participants (8)
-
Ashley Yakeley
-
Bas van Dijk
-
Edward Kmett
-
Henning Thielemann
-
Henning Thielemann
-
Isaac Dupree
-
Ross Paterson
-
Yitzchak Gale