Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods for converting from `bool` to `Option<T>` #2757

Open
wants to merge 4 commits into
base: master
from

Conversation

@varkor
Copy link
Member

commented Sep 5, 2019

Rendered.

The following pattern is prevalent in typical Rust code.

if some_condition {
    Some(t)
} else {
    None
}

Here, I propose new methods for converting a bool to an Option<T>, abstracting this pattern, to aid in readability and reducing repetition.

impl bool {
    fn then<T>(self, t: T) -> Option<T>;
    fn then_with<T, F: FnOnce() -> T>(self, f: F) -> Option<T>;
}

This supersedes #2180, which was closed due to concerns about the suggested names and resolves #2606.

text/0000-bool-to-option.md Outdated Show resolved Hide resolved
text/0000-bool-to-option.md Outdated Show resolved Hide resolved
@Centril

This comment has been minimized.

Copy link
Member

commented Sep 5, 2019

Adding the language team since the actual implementation of this requires the addition of a language item #[lang = "bool"]. This should also be noted in the reference text of the RFC.

@Centril

This comment has been minimized.

Copy link
Member

commented Sep 7, 2019

@varkor I think it would also be good to add a few examples of before/after for real world use-cases, e.g. in the compiler or elsewhere, that would have benefited from this RFC.

I think it would also be worth noting that this encourages the use of more closures. This ostensibly has the drawback of longer compile-times. On the flip-side, it also is a good idea due to the restrictions imposed on imperative control flow (return & friends). Such restrictions may improve local reasoning and is helpful towards creating closures that may be refactored into stand-alone functions over time. So the RFC may be a boon for maintainability.

@varkor

This comment has been minimized.

Copy link
Member Author

commented Sep 7, 2019

There's now a reference implementation at rust-lang/rust#64255.

@fbstj

This comment has been minimized.

Copy link
Contributor

commented Sep 7, 2019

I remember previous discussions on similar topics have regularly brought up the question of symmetry; aka "what about when self is false". is it something that should be mentioned/discussed in the RFC? is it future enhancement? is it worth adding the dual false.unthen() or should there be a false.not().then() or does !false.then() have the correct precedence (I believe not because it has to be looser than method calls for most things), or is (!false).then() acceptable?

@Centril

This comment has been minimized.

Copy link
Member

commented Sep 7, 2019

@fbstj https://doc.rust-lang.org/nightly/std/ops/trait.Not.html -- we could add it to the prelude if necessary.

I think foo.not().then(...) or (!foo).then(...) is fine.

@varkor

This comment has been minimized.

Copy link
Member Author

commented Sep 7, 2019

Very often you can rewrite a condition to avoid negation, and if that's not convenient (if the condition is used elsewhere, for instance), negation with ! or .not() seems no less readable, so I don't think we need extra methods for this.

@Centril

This comment has been minimized.

Copy link
Member

commented Sep 7, 2019

..., so I don't think we need extra methods for this.

And on the off chance we do, we are free to add more methods later, so this feels very safe. ;)

@Centril Centril added the I-nominated label Sep 7, 2019

Centril added a commit to Centril/rust that referenced this pull request Sep 7, 2019
Rollup merge of rust-lang#64255 - varkor:bool-to-option, r=Centril
Add methods for converting `bool` to `Option<T>`

This provides a reference implementation for rust-lang/rfcs#2757.
@troiganto

This comment has been minimized.

Copy link

commented Sep 7, 2019

I think the existing methods that are closest to the ones being proposed here are:

x.ok();  // Result<T, E> → Option<T>
x.err(); // Result<T, E> → Option<E>

x.ok_or(y);            // Option<T> → Result<T, E>
x.ok_or_else(closure); // Option<T> → Result<T, E>

x.and(y); x.and_then(closure); // Result<T, E> → Result<U, E>
x.or(y); x.or_else(closure);   // Result<T, E> → Result<T, F>

x.and(y); x.and_then(closure); // Option<T> → Option<U>
x.or(y); x.or_else(closure);   // Option<T> → Option<T>

I've noticed that the first four methods, which transform between different "monad kinds", explicitly mention the variant to transform to. The rest (and/and_then/or/or_else), on the other hand, stays within Option and Result respectively.

Based on this precedent, I think that methods which convert from bool to Option<T> should also contain the word "some" in their name, whatever the concrete choice is.

@varkor

This comment has been minimized.

Copy link
Member Author

commented Sep 8, 2019

I've noticed that the first four methods, which transform between different "monad kinds", explicitly mention the variant to transform to.

Unfortunately, they're not that consistent. Both the methods from Option to Result and Result to Option are named after the Result (i.e. ok and err). This naming scheme in general does not extend well to other type combinations. I read some people state that some would be confusing, because it's either Some or None.

@camlorn

This comment has been minimized.

Copy link

commented Sep 8, 2019

Seeing the name debate, I just want to ask why not to_option and to_option_with? I imagine this was discussed elsewhere, but in terms of "what does this do" when you don't know them, my_bool.then(5) is cryptic, my_bool.to_option(5) isn't. The former might have nice systemic math-like implications but the latter is very clear about what it does at least in my opinion.

@nugend

This comment has been minimized.

Copy link

commented Sep 11, 2019

Sorry, I skimmed through the discussion and didn't see it, but has something along the the following lines been considered? I realize that this goes back to putting the method on the Option rather than the bool and I didn't see if that had been ruled out entirely as unworkable in the earlier RFC.

Some(x).test(boolvar) // Some(x) or None

I don't know if there's a general rule against using test as a function name though (didn't see it in the keyword list). I was thinking ok might work as well, but then there's a small chance of confusion with ok_or and ok_or_else due to the different return types.

Presumably a lazy version would be a bit verbose though, which might make it a non-starter

Some(()).test(boolvar).and_then(|_| expensive_expression)
@joshtriplett

This comment has been minimized.

Copy link
Member

commented Sep 11, 2019

I'm not sure why this is tagged T-lang; this is entirely a libs decision.

@joshtriplett joshtriplett removed the T-lang label Sep 11, 2019

@joshtriplett

This comment has been minimized.

Copy link
Member

commented Sep 11, 2019

Ah, just saw @Centril's comment (which was marked as resolved).

For the sake of simplicity, I'd suggest treating the questions orthogonally; can we just ask if anyone on @rust-lang/lang objects to the creation of this lang item for bool, and if not, leave the rest of the decision entirely to T-libs?

@walfie

This comment has been minimized.

Copy link

commented Sep 11, 2019

As for the "prior art" section, Scala 2.13 has Option.when (and Option.unless for the inverse): https://www.scala-lang.org/api/2.13.x/scala/Option$.html

Option.when(true)(0) // Some(0)
Option.when(false)(0) // None

Though in this case the T value is always lazily evaluated. The equivalent in Rust would be something like:

Option::when(true, 0)
Option::when_with(true, || 0) // but probably with a less awkward-sounding function name
@Diggsey

This comment has been minimized.

Copy link
Contributor

commented Sep 11, 2019

I'm not a fan of then as the name because we typically use _then/_else as suffixes to indicate that a closure is used instead of a direct value. then_with seems particularly awkward.

Also, personally when I know I'm dealing with Option or Result types I'm already in the mindset to look for these kind of monadic operations, so names like and_then are not too surprising. This is not the case with bool.

@camlorn's suggestion of to_option/to_option_with seems much clearer.

Option::cond(true, 42) would be fine too.

@djc

This comment has been minimized.

Copy link
Contributor

commented Sep 11, 2019

I know I've wanted something like this a bunch of time.

Quick bikeshed addition: this also seems to be in line with how and_then() is used in other places.

@tesaguri

This comment has been minimized.

Copy link

commented Sep 11, 2019

@nugend We already have Option::filter that works just like your suggestion.

Some(x).filter(|_x| boolvar) // Some(x) or None
@Centril

This comment has been minimized.

Copy link
Member

commented Sep 13, 2019

For the sake of simplicity, I'd suggest treating the questions orthogonally; can we just ask if anyone on @rust-lang/lang objects to the creation of this lang item for bool, and if not, leave the rest of the decision entirely to T-libs?

@joshtriplett In the future, please talk to me privately if you want to remove T-Lang from something. I usually have a good reason for doing adding T-Lang.

I'm fine with the addition of the language item for the purposes of what this RFC wants to achieve (since I think it's a reasonable and consistent addition) but I'm not fine with the language item for whatever. However, this RFC should not be taken as precedent for relying on lang items without the T-Lang's approval.

@nugend

This comment has been minimized.

Copy link

commented Sep 14, 2019

@tesaguri Oh, good point!

I guess having it right on the bool would save some finger motion if you needed to write it over and over.

@varkor

This comment has been minimized.

Copy link
Member Author

commented Sep 16, 2019

I just want to ask why not to_option and to_option_with?

I'm happy with the names to_option and to_option_with, if there seems to be a rough consensus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.