Pre-SIP: Proposal of introducing a Rust-like type "Result"

#21

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 :slight_smile:

1 Like
#22

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)

3 Likes
#23

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.

1 Like
#24

That was a typo, I am unhappy with the semantics.

#25

Then how would the semantics differ from those of Either?

1 Like
#26

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.

1 Like
#27

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.

#28

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?

1 Like
#29

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.

#30

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!”

1 Like
#31

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 keeping Either is so bad.
8 Likes
#32

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.)

2 Likes
#33

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 :slight_smile:

1 Like
#34

Doesn’t partial unification in Scala work similarly?

5 Likes
#35

What exactly is the proposal? What would Result be?

#36

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.

1 Like
#37

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.

2 Likes
#38

Dotty does the same.

#39

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.

3 Likes
#40

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!?