Not sure if I can answer that on everyone else’s behalf. But I find it more useful than Either. I could be wrong and maybe a better name is there
Be careful that Result
is a fairly common term, certainly in wild
usage (at least, I use it in most of my projects, or declination of it).
Actually, I have a new convention to use PureResult[A]
as an alias for
Either[MyBusinessErrorSuperType, A]
and IOResult[A]
as an alias for
ZIO[Any, MyBusinessErrorSuperType, A]
.
(see https://issues.rudder.io/issues/14870 for context / explanation and
a brief summary of the tooling built on that)
If you don’t really want to rename Either
, and are happy with its semantics, then I’m kind of lost on what you do actually want.
That was a typo, I am unhappy with the semantics.
Then how would the semantics differ from those of Either
?
This would fit nicely within the “Summer of Usability”.
One thing that makes Scala difficult for beginners is the lack of clear best practices. Error handling is just one of these cases. How do you return errors, the Scala way? Throw errors? Return a Try
? Or use the more general Either
?
A Result
type with distinct Success
and Failure
cases (and maybe some supporting infrastructure) that’s used in the documentation and the tutorials would allow neophytes to not have to worry about this. Advanced programmers can still use Either
or roll their own.
I think “Result” is very intuitive, but it’s not just the name that makes “Result” something significant in Rust. First, there’s language syntax to support Result. Second, and most importantly, Result is consistently used across Rust libraries, so you just can’t avoid becoming aware of it and how it is used as soon as you start doing anything in Rust. In fact, Result is covered early on by the two Rust books I’ve seen.
I’m still not clear on how this Result
type would differ from Either
other than by name – does it have different semantics, and if so, what are those?
Well, I’d argue that Either
doesn’t really have any semantics at all - it’s just a disjoint union. This is what is makes it difficult for beginners, since as a return type it doesn’t tell you what the “success” is and what the “failure”. Right
is success by convention (ans since 2.12 b bias), but you have to memorize “left is failure, right is success”. It’s not exceptionally bad, it’s a paper cut. But a paper cut that could be avoided.
A Result
type with distinct Success
and Failure
cases would provide these semantics.
I’m not yet sure how Try
would fit in. Maybe a opaque alias with some additional machinery? Why use Try
instead of Result
?
I think the whole error handling in Scala deserves some thought about best practices, as I mentioned before.
One may argue that instead of Either/Right/Left it may have been better to call it Result/Success/Failure. But now that it is already well established, what to do?
(1) Renaming it is quite disruptive.
(2) Duplicating it seems wasteful and confusing (“What’s the difference?”)
(3) Aliasing works, but, can be a little mental overhead or confusion, too
Regarding semantics: if you look at Either, it is immediately clear that it can be used for Success/Failure scenarios.
It’s less clear that not only can it be used for that purpose, but it is actually widely used for it, i.e. no other alternative has established itself like it.
Remembering which value is success and which is failure is easy: “The Right value is the right value!”
My take is:
We unfortunately got ourselves into a bad mess with Either
. It is extremely unfriendly to beginners and confusing on many levels. In my mind, once you got the basics wrong, any sort of patching up only gets you deeper in the hole. For instance,
Either.cond(condition, a, b)
So, if condition
is true it gives you the left alternative wrapped in a Right
, otherwise the right alternative wrapped in a Left
. To which the only appropriate answer is a long rolling “Riiiight”.
The original sin for Either
was right biasing it. Before, Either
could indeed be regarded as just a tagged union, and any code that used it otherwise was dubious. But right-biasing Either
officialized
the mistake.
Interesting aside: Why did people pick Right
for the result and Left
for error? I believe it is because Haskell does it this way. And why did Haskell do it this way? Because of curried type applications. The error type is often more stable than the result type, so you want more often a partial type application Either E
than its dual Either R
. But Scala does not have curried type applications, so the only reason to do it that way does not apply.
I believe our two choices are:
- Keep
Either
as the only official “result” type, accepting that users will be confused forever, and that our only explanation on why things are that way is “because Haskell”. - Rip off the band aid, and introduce
Result
. The only possible moment where this is a valid option is when we switch to Scala 3. But even then it is a daunting task. Nevertheless, I am seriously tempted to do it since for somebody like me who teaches Scala a lot the alternative of keepingEither
is so bad.
You could just deprecate Either.cond. It doesn’t seem very useful anyway. In any case even with a right bias there’s a valid argument for making the arguments go from left to right. Having to negate a boolean is not the biggest crisis to worry about.
Anyway, I don’t see any benefit to left-biasing it, and history aside, choosing or explaining a right bias does not require invoking Haskell. I really don’t understand the problem.
For anyone that wants a left-biased type:
type Else[A, B] = Either[B, A]
Now you can annotate all your methods Thing Else Error
if you like.
Incidentally, I don’t think Result is a good name for a general-purpose type. Types shouldn’t care whether you use them as an input or as an output. The way Play uses Result is fine in this respect, since it denotes an HTTP response, which is specifically an output. (Why it isn’t called Response is a separate matter.)
For the Either
biased, my FP teachers used to tell us that it was because the right answer is Right
(and gauche
, ie left in French, used to mean bad
- a root you find also in Italian sinistra
, and of course it was for extremelly bad social reason but well).
OK, so for the Either
case: in Scala 3, we will get real disjoint union with sum type. A built in Either
for that case is no longer needed, and I bet Either
is rather rarely used for union, even just because it is so much used for a return type.
Nonetheless, ditching it for Result
in scala 3 will be extremelly disruptive. It’s a very common name, most likely used in a lot of places (for ex see my comment above Pre-SIP: Proposal of introducing a Rust-like type "Result"). And of course, it will mean rewrite of most of scala projects (the number of projects which don’t use Either
directly or because of lib API might be small… But I may be biased, perhaps having data on that would be helpfull).
That being said, I believe it would be a good thing to do.
For the semantic, please don’t make it like Try
, ie let effects alone. If you want to deal with effect, please make Result[E, A]
isomorphic to ZIO[Any, E, A]
, with a clear semantic to import effectful code into that result (ie Result.effect(chunk): Result[Throwable, A]
). But perhaps that we don’t want to standardise in the scala library something which is moving as fast as effect management in that Result
, which I would personnaly prefer, and so just change Either
into Result
(also: the migration in the case of effect-managing Result
would be extremelly hard because of the semantic change in presence of effects).
And then, a Result[E, A]
in the standard lib, any project will be able to do:
type PureResult[A] = Result[MyBusinessErrorSuperType, A]
type IOResult[A] = ZIO[Any, MyBusinessErrorSuperType, A]
And things will be so much clearer
Doesn’t partial unification in Scala work similarly?
What exactly is the proposal? What would Result be?
Just a rename of Either
and related methods to make them result-y (.mapLeft
should become .mapError
, .fold
should behave correctly with inference, etc. )
I would also love to have some syntaxic sugar to have .fail
and .succeed
(or .failure
and .success
) extension methods.
Yes, at least in Scala 2. The functor hole (i.e., the type parameter for success values) needs to be rightmost. (See SI-2712 for rather more detail, if you’re reading along and don’t know what we’re talking about.)
As far as I can tell we’re either (heh) asking for exactly Either
with different names, or maybe something like cats.data.Validated
which is isomorphic to Either
and exists only to select different typeclass instances (accumulating applicative instead of fail-fast monad … but I think the error-accumulating behavior will be awkward in vanilla Scala without applicative syntax, i.e., cats mapN
). In any case I couldn’t find a post with an actual proposal so I’m not 100% sure what we’re discussing.
My position for the moment is I don’t probably care as long as we keep Either
as-is (monadic, right-biased) and provide .toEither
and .toResult
to convert between the representations.
Dotty does the same.
Just my 2 cents. Either is perfectly fine, and it’s been right-biased since 2.12. Leave it be and don’t introduce additional complexity by adding a new type with the exact same semantics. We have much bigger fish to fry.
Holy S$#%!! In the amount of time we’ve spent debating this issue a small OSS library could have been written! Or all the needed adaptors from whatever implementation of Try/Either to whatever the heck else we want with similar functionally could have been written! Maybe let’s be a bit more pragmatic!?