[Library] enum Fallible on std
pub enum Fallible<T> {
Succ,
Fail(T),
}
how Option is equivalent to Result<T, ()>,
Fallible is equivalent to Result<(), T>
https://datavirke.dk/posts/fallible-missing-rust-error-handling/
This article you linked has a wrong understanding of unit type.
The author thinks the unit type is of no value, hence the success case of Result<(), E> contains no value. This is wrong.
A fact that the function returns already contains information. When the returned value is a unit type, it's simply that there's no additional information. But the information is already conveyed by the fact that it returns.
So it's pretty obvious Result<(), E> is perfectly right for the use case.
I don't think the article has any misconceptions about that, and the table is correct. But I also think that it is 8 years too late to be proposing something like this; the value of these kinds of types lies in their ubiquity and there is no way Fallible will ever gain that kind of traction. Option and Result have been in Rust from day 1.
I think it would be a bad idea to add something like this to the prelude, and lose names like Succ and Fail in user code. The meaning of this type is still essentially a success-or-fail, meaning that it will want to use the same terminology as Result and will largely overlap with it. Who wants to learn that failures return Err, unless the function doesn't return a success result in which case you should spell it Fail?
I think the only reasonable version of this would be if you had type Fallible<T> = Result<(), T> and const Succ: Fallible<T> = Ok(()), but that likely doesn't deliver on the main goals discussed in the article in the first place because it isn't first class in the same way as Option and Result.
If anything, it'd make more sense to have type Option<T> = Result<T, ()> instead. Maintaining the parity between Option and Result is costly.
This article you linked has a wrong understanding of unit type.
The author thinks the unit type is of no value, hence the success case of
Result<(), E>contains no value. This is wrong.A fact that the function returns already contains information. When the returned value is a unit type, it's simply that there's no additional information. But the information is already conveyed by the fact that it returns.
So it's pretty obvious
Result<(), E>is perfectly right for the use case.
unit contains one value, but in this case, a useless value
I don't think the article has any misconceptions about that, and the table is correct. But I also think that it is 8 years too late to be proposing something like this; the value of these kinds of types lies in their ubiquity and there is no way
Falliblewill ever gain that kind of traction.OptionandResulthave been in Rust from day 1.I think it would be a bad idea to add something like this to the prelude, and lose names like
SuccandFailin user code. The meaning of this type is still essentially a success-or-fail, meaning that it will want to use the same terminology as Result and will largely overlap with it. Who wants to learn that failures returnErr, unless the function doesn't return a success result in which case you should spell itFail?I think the only reasonable version of this would be if you had
type Fallible<T> = Result<(), T>andconst Succ: Fallible<T> = Ok(()), but that likely doesn't deliver on the main goals discussed in the article in the first place because it isn't first class in the same way asOptionandResult.
8 years ago very few people knew Rust, I was just starting to learn programming with C and PHP
I understand the difficulties in implementing, but to say that Succ and Fail can't be added to prelude presuppose that nothing more can be added to prelude, it's the case?
I don't think the article has any misconceptions about that, and the table is correct. But I also think that it is 8 years too late to be proposing something like this; the value of these kinds of types lies in their ubiquity and there is no way
Falliblewill ever gain that kind of traction.OptionandResulthave been in Rust from day 1.8 years ago very few people knew Rust, I was just starting to learn programming with C and PHP
That wasn't directly aimed at you (obviously everyone has their own circumstances), but rather the proposal itself: Rust too stable now to be able to make foundational changes like this anymore. A proposal that tries to make Fallible a completely new type with a parallel API will not work because there is a whole ecosystem that would have to adapt to it, and in many cases that would mean breaking API changes in places where breaking changes are unacceptable.
I understand the difficulties in implementing, but to say that
SuccandFailcan't be added to prelude presuppose that nothing more can be added to prelude, it's the case?
No, but the bar for getting things into the prelude is very high. So far the prelude has only changed once, for the 2021 edition, and even then most of the originally planned additions were cut before the release; and everything considered was already in std and used widely in the ecosystem. Going from nothing at all to in the prelude is a huge jump and would take many years.
@digama0 I meant that at that time Rust was irrelevant in a broader context (break changes weren't a problem like they are today) and influenced by few people, much less than today
Well, it's the Haskell's lemma of not being popular, Rust is getting more bureaucratic, that was good, but now is difficulting its improvement
@digama0 I meant that at that time Rust was irrelevant in a broader context (break changes weren't a problem like they are today) and influenced by few people, much less than today
Right. That's why such a change would have been okay then, but not now, even if the proposal were exactly the same.
But even setting that aside, assuming we could magically solve the compatibility and ecosystem issues, I think there are other significant issues with the proposal like having Err and Fail which are synonyms and would confuse newcomers, in addition to refactoring / inlining functions returning Result<T, E> into Fallible<E> and back now having additional overhead because if the return value changes then you have to change all the Err to Fail and Ok to Success.
Another way to put it: Option and Result are monads, Fallible isn't. This means that Result handles composition better than Fallible, and it is also an explanation for why it doesn't show up in ML or Haskell and why Rust inherited the same legacy. For Fallible to have an and_then operator, it would need to use Result for some of the intermediates, but if they are () then you want to use Fallible instead, so that's ... 4 different versions of and_then? The naming would be a mess, and switching from one to another on a refactor would also be annoying. And why, when we have generics and a perfectly good instance of Result to use to cut down on the duplication?
@digama0 why Fallible can't be a monad?
A monad is a type constructor M<T> with an operation pure: fn(T) -> M<T>, and_then: fn(M<A>, fn(A) -> M<B>) -> M<B> and some laws. Now Fallible<T> is also a type constructor so maybe you might consider to take M<T> := Fallible<T>. One of the monad laws is pure(x).and_then(f) = f(x), which implies that pure(x) = Success would not work, and pure(x) = Fail(x) would imply that and_then runs the failing result and not the successful result, making it equivalent to Option and not having the desired short-circuiting behavior on ?.
I would say that Option and Result have completely separate use cases, and thinking about Option<T> as an equivalent for Result<T, ()> is a bad idea.
-
Resultis used for error handling. -
Optionis used when a value may or may not exist.
For example: Iterator::next returns an Option to indicate that a next item may not exist. The absence of a next item is not an error; it just means that the iteration is finished.
Likewise, Some does not imply success. For example, an error may contain an Option<Backtrace>. If it is Some, then we have more information about the error, but it is still an error.
I would say that
OptionandResulthave completely separate use cases, and thinking aboutOption<T>as an equivalent forResult<T, ()>is a bad idea.
They aren't saying they have the same use case or that they are the same thing, they are saying that they have the same footprint and are interchangable. They both convey the same amount of information. The unit type conveys no extra information other than that exists, just like the None variant. You can convert any Result<T, ()> to an Option<T> and vice versa without any data loss.
Basically, Option<T> could be a type alias for Result<T, ()>.