I agree with this proposal. I also strongly agree with spir that we should
distinguish programmer errors from legitimate result possibilities. A
function should only fail directly if, were it to return a `None` or `Err`,
the only reasonable the thing caller could do would be to fail itself.

Somewhat formally, a function has a domain and a codomain, i.o.w. the
possible input and output values which make sense. If the type used for the
domain is "too big", and allows values which don't make sense in the
context of the function, then this opens up the possibility that the
programmer might mess up and nonetheless pass in one of those values. In
this case the right thing is for the function to fail at runtime: there's
not much else it could do. (This is the null argument /
NullPointerException situation in many other languages.)

The converse case is if the codomain is too small, and doesn't provide a
way to represent all of the possible output values. For example, a search
function might not have a way to say "not found". In this case the function
might also want to fail. But in Rust's type system it's much, much, much
easier to make types bigger than it is to make them smaller, so there's no
excuse not to do so. This function should just return an Option.

Of course, it's not always black and white, and what "makes sense" can
depend on the situation. Sometimes if the container we pass to the search
function doesn't contain the value we're searching for, then it's a bug.
The programmer can always signal this by just calling unwrap() (which I
agree should have a better name, if we can find one) or expect(). But I
think it's also reasonable to add convenience functions if this would be
the case often enough to make them worthwhile. But they should be clearly
presented as "added convenience functions, which fail in [these cases]" and
not as the default option.

Haskell has a bunch of partial functions which do their equivalent of
failing in their Prelude, such as `head :: [a] -> a`, and they've been
regretting it ever since. IMHO, let's try to keep our functions total
whenever we possibly can.


On Fri, Dec 6, 2013 at 9:41 PM, Simon Sapin <[email protected]> wrote:

> We have some functions and methods such as [std::str::from_utf8](http://
> static.rust-lang.org/doc/master/std/str/fn.from_utf8.html) that may
> succeed and give a result, or fail when the input is invalid.
>
> 1. Sometimes we assume the input is valid and don’t want to deal with the
> error case. Task failure works nicely.
>
> 2. Sometimes we do want to do something different on invalid input, so
> returning an `Option<T>` works best.
>
> And so we end up with both `from_utf8` and `from_utf8`. This particular
> case is worse because we also have `from_utf8_owned` and
> `from_utf8_owned_opt`, to cover everything.
>
> Multiplying names like this is just not good design. I’d like to reduce
> this pattern.
>
> Getting behavior 1. when you have 2. is easy: just call `.unwrap()` on the
> Option. I think we should rename every `foo_opt()` function or method to
> just `foo`, remove the old `foo()` behavior, and tell people (through
> documentation) to use `foo().unwrap()` if they want it back?
>
> The downsides are that unwrap is more verbose and gives less helpful error
> messages on task failure. But I think it’s worth it.
>
> What do you think?
>
> (PS: I’m guilty of making this worse in #10828, but I’d like to discuss
> this before sending pull requests with invasive API changes.)
>
> --
> Simon Sapin
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev
>



-- 
Your ship was destroyed in a monadic eruption.
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to