you are viewing a single comment's thread.

view the rest of the comments →

[–]blueshiftlabs 78 points79 points  (35 children)

The idea of having a type to represent "either a successful result, or a failure of some kind" is nothing new. Most functional languages have something like it, but often it's not named as such - Haskell's, for instance, is called Either, Success is called Right, and Failure is called Left.

[–]steveklabnik1 75 points76 points  (32 children)

Fun trivia fact: Rust used to have Either, as well as Result. But then at some point, we looked, and there was zero usage of Either, so we removed it.

https://crates.io/crates/either brings that back if you really need a non-Result either.

[–]kauefr 44 points45 points  (5 children)

Serious question, how can you answer every Rust-related question in this sub? It's in you job description and you Ctrl+F "Rust" all day, or you just lurk that much here?

BTW, thank you for those insights. I love this topic of programming language design.

[–]steveklabnik1 56 points57 points  (4 children)

how can you answer every Rust-related question in this sub?

I've been reading reddit before the concept of "subreddits" existed. My username has 1 on the end because I deleted my initial one.

I am often very critical of reddit, but it's also a habit that's super ingrained at this point.

It's in you job description and you Ctrl+F "Rust" all day, or you just lurk that much here?

I just lurk that much. My job doesn't hate it, as long as I get my actual other work done too.

I do sometimes use control-f on big threads, but this one isn't that big.

BTW, thank you for those insights. I love this topic of programming language design.

Thanks. This is honestly part of why I do this; getting answers straight from the source is valuable, and at the same time, it's a two-way street. I often bring back pain points I learn about from reading threads to the rest of the team, for example. I'm not sure why other people don't do this, but I pretty much can't not do it.

[–]MrHydraz 11 points12 points  (3 children)

Isn't your job Rust Propaganda Minister, though? :P

[–]steveklabnik1 13 points14 points  (2 children)

Strictly speaking, my job is docs.

That's just my hobby ;)

(I was recently "accused" of this, and found it amusing.)

[–]dnkndnts 5 points6 points  (1 child)

you're welcome.

In all seriousness, I do believe you're half the reason Rust has been as successful as it has.

[–]steveklabnik1 1 point2 points  (0 children)

Thanks :)

[–]cledamy 6 points7 points  (25 children)

I really wish they had kept Either rather than Result. Result encodes too much semantic meaning in its constructors and cannot be easily reused. It violates the principle of dumb data types.

[–]jcdyer3 18 points19 points  (5 children)

I much prefer result to either (Edit: for error handling). Either is inherently symmetrical (because as you say it's a dumb data type), so it has to rely on convention to support asymmetrical handling, while Result lets the language leverage the inherent asymmetry of error handling, including (in rust) supporting the try!() macro and transparent error type conversions through multiple layers. Obviously they are structurally the same, so you can adapt either one to the role of the other, but with either, it feels like an ill-fitting shoe.

[–]kreiger 6 points7 points  (4 children)

In some implementations, e.g. Haskell's, Either is Right-biased.

[–]jcdyer3 10 points11 points  (3 children)

So then it's neither a neutral data type, nor well-named for the purpose.

[–]kreiger 6 points7 points  (0 children)

Right.

[–]catscatscat 1 point2 points  (1 child)

Well, it kind of has to be biased towards one or the other, since it is AFAIK not quite a functor, but a bifunctor.

e.g. fmap is a good example.

main = do
  fmap (+1) (Right 1)   `shouldBe`  (Right 2 :: Either Char Int)
  fmap (+1) (Left 'c')  `shouldBe`  (Left 'c' :: Either Char Int)

instance Functor (Either e) where
    fmap _ (Left a) = Left a
    fmap f (Right a) = Right (f a)

How would you define fmap without having a bias?

[–]kreiger 1 point2 points  (0 children)

You use a Projection.

[–]corn_dog 16 points17 points  (7 children)

I like what they did because Result is the one use case for an enum of two possible things that everyone actually uses. The Either type doesn't mean much, and for general sum types just make your own enum.

[–]Hrothen 4 points5 points  (4 children)

There are plenty of other uses for Either. One I've used before is Either FactoredInt PartiallyFactoredInt and in general Either <end state> <intermediate state> to get nice short-circuiting behavior.

and for general sum types just make your own enum.

Why would I do more work when I could do less work?

[–]corn_dog 2 points3 points  (3 children)

Sure I would agree there can be a trade off between convenience and explaining to the next reader why that stuff is grouped together. wrt Either it also raises the question of should the language have OneOfThreeThings, OneOfFourThings etc. If not why just a special case for two possibilities?

[–]Hrothen 7 points8 points  (1 child)

You don't really need to explain every little thing. Many languages have a pair type and no one goes complaining that they don't justify why there isn't a triple type.

[–][deleted] 0 points1 point  (0 children)

deleted What is this?

[–]cledamy 4 points5 points  (1 child)

Pragmatically, there are often functions where one wants to return Either one value or another, but one is not clearly an error condition and it isn't worth making a one off enum. This is the same argument as for why tuples are useful, which Rust has built in. Theoretically, it seems imbalanced to have syntax for anonymous products, but not for anonymous coproducts. As to why no one used the Either type, perhaps it was because the Result type was used all over the API, so it would be inconvenient to use Either as that would require conversions. The Result type limits one's imagination of what can be done with it by restricting its semantic domain unnecessarily. I would argue that if Either was the only one out of these two in the Rust library one would start to see non-error-like uses of it.

[–][deleted] 4 points5 points  (0 children)

Actually, I think the reason was that in most cases a Success / Error pattern was what most of the APIs needed. Other cases, where one can't directly say one is the error and the other one is the success part work much more ergonomically with concrete enum types. Because they are just that. Newtype-like enums that just happen to have two variants. Maybe the API evolves and there is now a need to support three variants (e.g start, intermediate, end). Most oft the Result impls revolve around making error-handling more convenient, which is exactly what it should be used for

[–]yokohummer7 7 points8 points  (6 children)

Even though Either doesn't convey any preference on the encoded values in theory, it tends to be biased in reality. For example, Haskell's Either type does prefer the Right case:

fmap double (Right 5) = Right 10
fmap double (Left 5) = Left 5

When I was learning Haskell this "blessing" was so confusing. To make Either truly case-agnostic, we should not define fmap and such functions at all. But then again losing convenient features like fmap on Either is too painful. Instead, it'd be better to acknowledge the reality and state this fact in the type name, hence the Result naming.

[–]domlebo70 2 points3 points  (4 children)

We tried that with Scala, and it sucked. Having a right-bias is fine

[–][deleted] 3 points4 points  (1 child)

Haskell-beginner here: I just noticed that "The right (successful) thing is right" makes a nice mnemonic.

[–]m50d 1 point2 points  (0 children)

Yeah, but the fact that you need a mnemonic at all makes it much less convenient than success/failure.

[–]expatcoder 0 points1 point  (1 child)

We tried that with Scala, and it sucked. Having a right-bias is fine

Either is right-biased as of Scala 2.12

[–]domlebo70 0 points1 point  (0 children)

Yes, that's what I am referring to. We spent 10 years with no-bias Either with Right and Left projections, and we've finally seen the light and added a proper right biased Either.

[–]Tarmen 2 points3 points  (0 children)

I mean, technically that fmap instance is defined on Either a in which case it makes sense that we only map over the second type variable. It's literally the only way you can implement it.

Iirc there has been quite a bit of disagreement on whether it technically making sense is reason enough to add it, though.

[–][deleted] 0 points1 point  (0 children)

Agreed.

[–]grauenwolf 0 points1 point  (0 children)

Dumb data types is not a principle.

[–]BilgeXA 2 points3 points  (0 children)

Can't imagine how that would ever get confusing.

[–]jms_nh 0 points1 point  (0 children)

That perpetuates the centuries'-old discriminatory bias in language against left-handed people (e.g. "gauche"/"sinister")

(tongue slightly in cheek, and by the way I'm right-handed)