-
Notifications
You must be signed in to change notification settings - Fork 1.6k
RFC: Imply Option #2180
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
RFC: Imply Option #2180
Changes from 2 commits
d7e0c0d
71adf99
99ec896
234a07f
8c0c7eb
15c315c
87e00cf
d30578e
c87c398
d9c9a08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| - Feature Name: imply-option | ||
| - Start Date: 2017-10-18 | ||
| - RFC PR: (leave this empty) | ||
| - Rust Issue: (leave this empty) | ||
|
|
||
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| This is an RFC to reduce common boiler plate code when making use of the `Option` type. Similar in intention and motivation to the `Try` trait for `Result`. | ||
|
|
||
| # Motivation | ||
| [motivation]: #motivation | ||
|
|
||
| This addition will increase the legability of code segments and assist in defining the thought processes and motivations of programmers through code. The use cases of this addition are solutions which are expressable in the following predicate form: | ||
|
||
| ``` | ||
| P(x) : Predicate on `x`. | ||
| F(y) : Function on `y` | ||
| P(x) -> F(y) | ||
|
||
| ``` | ||
| Or the following Rust psudocode: | ||
|
||
| ``` | ||
| if P(x) { | ||
| Some(F(y)) | ||
| } else { | ||
| None | ||
| } | ||
| ``` | ||
| The outcome of this addition will reduce repeated code which introduces bugs during refactoring and present the thought process of the programmer in a clearer fasion through their code. | ||
|
||
|
|
||
| # Guide-level explanation | ||
| [guide-level-explanation]: #guide-level-explanation | ||
|
|
||
| The `Option` type is extremely useful when your code may or may not yield a return value. | ||
| Such code may looks similar to this: | ||
| ``` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| let x = 0; | ||
|
|
||
| if x == 0 { | ||
| Some(x) | ||
| } else { | ||
| None | ||
| } | ||
| ``` | ||
| However only the `if` branch of this code segment is the important part we're concerned about in our code: | ||
| ``` | ||
| if x == 0 { | ||
| Some(x) | ||
| } | ||
| ``` | ||
| But the `else` branch is required for returning `None` value if `x == 0` evaluates to false. | ||
| Fortunately Rusts `Option` type has functionality get rid of the unecessary code: | ||
| ``` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing a |
||
| let x = 0; | ||
|
|
||
| Option::on_pred(x == 0, x) | ||
| ``` | ||
| This code performs the exact same function as our original `if` statement however our code is compressed to a single line and our intentions are just as clear. | ||
|
||
| Have you spotted the possible issue with this solution introduces however? What about this code: | ||
| ``` | ||
| Option::on_pred(false, foo()) | ||
| ``` | ||
| The above line of code will always return `None` and always throw away the result of `foo()` wasting our precious computing power every time our code needs to return `None`. | ||
| Rust has thought ahead of this problem though: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This still suggests rust is perfect already and already includes this fancy method below which solves everything. If the proposal for this RFC is to add 2 methods, please state that in the summary with a single line description for each.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again that is the format which is requested in the RFC template of |
||
| ``` | ||
| Option::lazy_pred(false, foo) | ||
| ``` | ||
| `Option`s `lazy_pred` function leverages lazy evaluation by taking a function pointer as its second argument. If its first argument evaluates to `true` it will return `Some(foo())` but if its first argument is `false` it returns `None` without having to run `foo`. This solves the problem presented in our earlier example without sacrificing the advantages it gave us. | ||
|
||
|
|
||
| # Reference-level explanation | ||
| [reference-level-explanation]: #reference-level-explanation | ||
|
|
||
| This addition is in essence an implementation of the "implies" or "->" operation from predicate logic. | ||
| In predicate logic, for those unfamiliar, the "->" operation has the following truth table: | ||
|
||
| ``` | ||
| | x | y | x -> y | | ||
| | F | F | T | | ||
| | F | T | T | | ||
| | T | F | F | | ||
| | T | T | T | | ||
| ``` | ||
| The Rust addition I am suggesting can be encapsulated as "If `x` is `true`, I care about the value of `y`; else I do not care about the value of `y`." or: | ||
|
||
| ``` | ||
| | x | x -> y | | ||
| | F | None | | ||
| | T | Some(y)| | ||
| ``` | ||
| My initial proposal for how this addition could be implemented is: | ||
|
||
| ``` | ||
| impl<T: Sized> Option<T> { | ||
| /// A straight forward implementation of the `implies` operation for predicate logic. | ||
| fn on_pred(pred: bool, value: T) -> Self { | ||
| if pred { | ||
| Some(value) | ||
| } else { | ||
| None | ||
| } | ||
| } | ||
| /// A lazy implementation of the `implies` operation when necessary. | ||
| fn lazy_pred<F>(pred: bool, func: F) -> Self | ||
| where F: FnOnce() -> T { | ||
| if pred { | ||
| Some(func()) | ||
| } else { | ||
| None | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| This implementation covers the use cases I've proposed in my earlier examples and any others of similar form without any external dependancies; this should make the implementation stable as Rust continues to develope. | ||
|
||
|
|
||
| # Drawbacks | ||
| [drawbacks]: #drawbacks | ||
|
|
||
| This is a functionality which has functional programming and monads in mind with its design and this may make it another stepping stone to be learned for programmers which are new to Rust or functional programming concepts. | ||
|
||
|
|
||
| # Rationale and alternatives | ||
| [alternatives]: #alternatives | ||
|
|
||
| The implementation I've proposed is clear and easily documented and is the minimal ammount of code and change necessary to add this into the Rust language without sacrificing any of the advantages of the `if/else` blocks. | ||
|
||
| Other designs which have been considered are: | ||
| - Not including the `on_pred` function. However this adds additional boiler plate code to make use of the functionality when passing in a value directly: | ||
| ``` | ||
| let x = 0; | ||
|
|
||
| //Option::on_pred(true, x) | ||
| Option::lazy_pred(true, || x) | ||
| ``` | ||
| It is very little boiler plate code compared to the `if/else` alternative but it is suboptimal from an execution standpoint and a more obtuse implementation for new Rust programmers to learn. | ||
| - Not including the `lazy_pred` function. However, as discussed, this leaves the `on_pred` function at a disadvantage when the equivilant `if` block is computationally intesive as it wastes computation on a value which may simply be discarded. | ||
|
||
| - Providing syntax support for this implementation in Rust (similar to the `?` operator for the `Result` type). However I argue that pushing the abstraction of the logic this far reduces the clarity of the code and the expression of the programmers intention. Additionally discussion has yet to addiquately cover syntax support for both the `on_pred` and `lazy_pred` functions in a meaningful manner and removing either one is disadvantages as discussed above. | ||
|
||
|
|
||
| # Unresolved questions | ||
| [unresolved]: #unresolved-questions | ||
|
|
||
| Through the RFC process I hope to qualify: | ||
| - That this is first a problem which does affect other Rust programmers. | ||
| - That my proposed solution would meaningfully improve the experience of other programmers in Rust. | ||
| - That my proposed implementation cannot be further optimised or stabilised. | ||
| As mentioned under the [alternatives] section syntax support of this feature is a possibility in future but I feel is outside the scope of this RFC before the implementation is stabilised in Rust and a meaningful syntax for this feature is yet to be determined. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reduce boiler plate by doing what? Adding methods? Adding syntax? Changing syntax?