I think one of the cool things about breaking out MonadFail would be the opportunity to introduce an instance for Either String, which isn’t possible to do with plain Monad without overlapping instances. The instance itself would be completely trivial:
instanceMonadFail(EitherString)wherefail=Left
The only possible reason to not do this, as far as I can tell, would be because it requires FlexibleInstances. This already seems to be used for a few instances in base, and it seems like it would be extremely useful, so I would really appreciate if such an instance was defined. I’d be happy to submit a patch to add it, but I wasn’t sure if this would be an uncontroversial change or not.
Trac metadata
Trac field
Value
Version
8.0.1
Type
FeatureRequest
TypeOfFailure
OtherFailure
Priority
normal
Resolution
Unresolved
Component
libraries (other)
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Child items
0
Show closed items
No child items are currently assigned. Use child items to break down this issue into smaller parts.
Sounds like a good idea to me, but there might be issues (such as breaking compatibility with the current Monad instance for Either). I suggest you propose this formally, as described in https://wiki.haskell.org/Library_submissions, and see what the collective wisdom of the community thinks of this.
Ah, that’s a good point, though I don’t think I agree with it. I’m imagining text defining a MonadFail (Either Text) instance, for example, and it seems like it wouldn’t be all that worth it to prevent that kind of instance just to help out type inference in a couple cases.
Do you have a use case? I don't think details of string conversion belong in the MonadError instances and if you want instances like MonadFail (Either ByteString) that brings up issues of encoding, truncation (as IsString ByteString).
It would complicate the type class and make inference fail in basic examples
ghci> do Bar a <- Right (Foo 42)ghci| pure aghci| <interactive>:13:1: error: • Ambiguous type variable ‘a0’ arising from a use of ‘print’ prevents the constraint ‘(Show a0)’ from being solved. ...
instanceIsStringstr=>MonadFail(Eitherstr)wherefail::String->Eitherstrafail=Left.fromString-- Left "Pattern match failure in do expression at /tmp/tH2v.hs:16:3-7"str::EitherStringStringstr=doBara<-Right(Foo42);purea-- Left "Pattern match failure in do expression at /tmp/tH2v.hs:21:3-7"txt::EitherTextStringtxt=doBara<-Right(Foo42);purea-- Left (Const (Identity "Pattern match failure in do expression at /tmp/tH2v.hs:26:3-7"))cnst::Either(Const(Identity[Char])())Stringcnst=doBara<-Right(Foo42);purea
Your point about just using IsString makes sense to me, so maybe that would be better, instead. One of the reasons MonadFail might sometimes be desirable over MonadError is that MonadError does not make it possible to make a single expression generic over both EitherandIO, unless the Either type is Either IOException a.
The danger here is that the (Either String) specific instance proposed here rules out reasoning uniformly about any other instance someone might want to put on Either that works for more types, which could get to be be common in things like test suites.
This lets one 'pointwise' instance override anybody's ability to grab a more general one. e.g. the old Error class from MonadError approach. I'm not advocating for that class per se, as there is no need for noMsg for MonadFail, making that class "too big".
IsString on the other hand, does have the remarkable property that it has precisely the right size.
So if I understood you right, ekmett, you would be fine with Iceland_jack's approach, i.e. instance IsString str => MonadFail (Either str)? In that case, I'd be glad to assemble a patch provided there are no other concerns remaining.
I support the instance, but this should go out as a real libraries proposal to the libraries@ mailing list, rather than being decided and acted on in relative isolation here. If you want to kickstart that process I'll happily toss in a +1.
I tried implementing this, however, simply adding the instance in Data.Either leads to an import cycle because Data.String(IsString(..)) has to be imported:
Module imports form a cycle: module ‘Data.Either’ (libraries/base/Data/Either.hs) imports ‘Data.String’ (libraries/base/Data/String.hs) which imports ‘Data.List’ (libraries/base/Data/List.hs) which imports ‘Data.Traversable’ (libraries/base/Data/Traversable.hs) which imports ‘Data.Either’ (libraries/base/Data/Either.hs)
However, I can't investigate this further atm, so if anyone wants to pick this up, please go ahead. I guess some shuffling around (maybe creating a hidden module for IsString) should solve the issue.