From d7e0c0d7ea422c2f1b1d500829a2406ce7392456 Mon Sep 17 00:00:00 2001 From: Dynisious Date: Wed, 18 Oct 2017 17:11:45 +1100 Subject: [PATCH 01/10] Imply Option My RFC for adding some function implimentation for the Option type akin to the `implies` operation from predicate logic. --- text/0000-imply-option.md | 117 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 text/0000-imply-option.md diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md new file mode 100644 index 00000000000..9d9ee0d0559 --- /dev/null +++ b/text/0000-imply-option.md @@ -0,0 +1,117 @@ +- 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: + 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: + 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: + 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 Option { + /// 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(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. From 71adf999d5f7689ff6dc7d66197dc42e756a2649 Mon Sep 17 00:00:00 2001 From: Dynisious Date: Wed, 18 Oct 2017 17:42:52 +1100 Subject: [PATCH 02/10] Formatting Formatted poorly displayed code and pseudo-code to display correctly and be legible. --- text/0000-imply-option.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md index 9d9ee0d0559..df880ff6a30 100644 --- a/text/0000-imply-option.md +++ b/text/0000-imply-option.md @@ -12,15 +12,19 @@ This is an RFC to reduce common boiler plate code when making use of the `Option [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 @@ -28,6 +32,7 @@ The outcome of this addition will reduce repeated code which introduces bugs dur The `Option` type is extremely useful when your code may or may not yield a return value. Such code may looks similar to this: +``` let x = 0; if x == 0 { @@ -35,21 +40,30 @@ Such code may looks similar to this: } 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: +``` 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: +``` 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 @@ -57,16 +71,21 @@ Rust has thought ahead of this problem though: 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 Option { /// A straight forward implementation of the `implies` operation for predicate logic. fn on_pred(pred: bool, value: T) -> Self { @@ -86,6 +105,7 @@ My initial proposal for how this addition could be implemented is: } } } +``` 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 @@ -99,10 +119,12 @@ This is a functionality which has functional programming and monads in mind with 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. From 99ec8963ec24a717e57c1b3f782d3e0532492d8d Mon Sep 17 00:00:00 2001 From: Dynisious Date: Thu, 19 Oct 2017 07:39:34 +1100 Subject: [PATCH 03/10] Spelling Corrections Fixed some spelling errors spotted by @Centril --- text/0000-imply-option.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md index df880ff6a30..4187fcbc92a 100644 --- a/text/0000-imply-option.md +++ b/text/0000-imply-option.md @@ -11,7 +11,7 @@ This is an RFC to reduce common boiler plate code when making use of the `Option # 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: +This addition will increase the legibility 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` @@ -25,12 +25,12 @@ Or the following Rust psudocode: 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. +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 fashion 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. +The `Option` type is useful when your code may or may not yield a return value. Such code may looks similar to this: ``` let x = 0; @@ -48,13 +48,13 @@ However only the `if` branch of this code segment is the important part we're co } ``` 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: +Fortunately Rusts `Option` type has functionality to get rid of the unecessary code: ``` 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. +This code has the exact same behaviour as our original `if` statement. Our code is however 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()) @@ -69,7 +69,7 @@ Rust has thought ahead of this problem though: # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This addition is in essence an implementation of the "implies" or "->" operation from predicate logic. +This addition is in essence an implementation of the "implies" or "->" operation from First-order logic. In predicate logic, for those unfamiliar, the "->" operation has the following truth table: ``` | x | y | x -> y | @@ -78,13 +78,13 @@ In predicate logic, for those unfamiliar, the "->" operation has the following 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: +The Rust addition this RFC suggests 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: +This RFCs initial proposal for how this addition could be implemented is: ``` impl Option { /// A straight forward implementation of the `implies` operation for predicate logic. @@ -106,7 +106,7 @@ My initial proposal for how this addition could be implemented is: } } ``` -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. +This implementation covers the use cases proposed in the earlier examples and any others of similar form without any external dependencies; this should make the implementation stable as Rust continues to develop. # Drawbacks [drawbacks]: #drawbacks @@ -116,7 +116,7 @@ This is a functionality which has functional programming and monads in mind with # 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. +The implementation 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: ``` @@ -126,8 +126,8 @@ Other designs which have been considered are: 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. +- Not including the `lazy_pred` function. However, as discussed, this leaves the `on_pred` function at a disadvantage when the equivalent `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, 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 adequately cover syntax support for both the `on_pred` and `lazy_pred` functions in a meaningful manner and removing either one is disadvantageous as discussed above. # Unresolved questions [unresolved]: #unresolved-questions From 234a07f89d2d3d8a53c62abb435a7d2d68dfbde3 Mon Sep 17 00:00:00 2001 From: Dynisious Date: Mon, 13 Nov 2017 16:44:43 +1100 Subject: [PATCH 04/10] Improved Explanations Expanded some areas of the RFC to better explain some areas which were confusing contributors. --- text/0000-imply-option.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md index 4187fcbc92a..8f6de357304 100644 --- a/text/0000-imply-option.md +++ b/text/0000-imply-option.md @@ -6,18 +6,25 @@ # 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`. +This is an RFC to reduce common boiler plate code when making use of the `Option` type, providing two functions +``` + /// Construct `Some(T)`, conditionally on a boolean. + Option::on_pred(bool, T) + /// A lazy equivalent of `on_pred`. + Option T>::lazy_pred(bool, F) +``` +Similar in intention and motivation to the `Try` trait for `Result`. # Motivation [motivation]: #motivation -This addition will increase the legibility 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: +This addition will increase the legibility of code segments and assist in defining the thought processes and motivations of programmers through code. The use cases of this addition are problems 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: +Or the following Rust pseudocode: ``` if P(x) { Some(F(y)) From 8c0c7eb5b1e8f52ce4aa0a83c1800952e08e17a0 Mon Sep 17 00:00:00 2001 From: Dynisious Date: Mon, 13 Nov 2017 16:53:46 +1100 Subject: [PATCH 05/10] Syntax Error My definitions in the summary didn't have semicolons. Not exactly an earth shattering issue but my as well fix it up. --- text/0000-imply-option.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md index 8f6de357304..8f0ceb21129 100644 --- a/text/0000-imply-option.md +++ b/text/0000-imply-option.md @@ -9,9 +9,9 @@ This is an RFC to reduce common boiler plate code when making use of the `Option` type, providing two functions ``` /// Construct `Some(T)`, conditionally on a boolean. - Option::on_pred(bool, T) + Option::on_pred(bool, T); /// A lazy equivalent of `on_pred`. - Option T>::lazy_pred(bool, F) + Option T>::lazy_pred(bool, F); ``` Similar in intention and motivation to the `Try` trait for `Result`. From 15c315ceb031e5650b475aae3e8e7e0a7bf2e028 Mon Sep 17 00:00:00 2001 From: Dynisious Date: Mon, 13 Nov 2017 17:09:42 +1100 Subject: [PATCH 06/10] More Syntax More syntax fixing, I'm tired and it's 33 Celsius here. --- text/0000-imply-option.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md index 8f0ceb21129..b0adc598ce9 100644 --- a/text/0000-imply-option.md +++ b/text/0000-imply-option.md @@ -9,9 +9,9 @@ This is an RFC to reduce common boiler plate code when making use of the `Option` type, providing two functions ``` /// Construct `Some(T)`, conditionally on a boolean. - Option::on_pred(bool, T); + fn Option::::on_pred(bool, T); /// A lazy equivalent of `on_pred`. - Option T>::lazy_pred(bool, F); + fn Option:: T>::lazy_pred(bool, F); ``` Similar in intention and motivation to the `Try` trait for `Result`. From 87e00cfd145ef68f8fe70bfbd9436485508c58aa Mon Sep 17 00:00:00 2001 From: Dynisious Date: Sun, 3 Dec 2017 00:49:34 +1100 Subject: [PATCH 07/10] Update 0000-imply-option.md --- text/0000-imply-option.md | 80 ++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 47 deletions(-) diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md index b0adc598ce9..69b00310001 100644 --- a/text/0000-imply-option.md +++ b/text/0000-imply-option.md @@ -8,31 +8,26 @@ This is an RFC to reduce common boiler plate code when making use of the `Option` type, providing two functions ``` - /// Construct `Some(T)`, conditionally on a boolean. - fn Option::::on_pred(bool, T); - /// A lazy equivalent of `on_pred`. - fn Option:: T>::lazy_pred(bool, F); + impl OptionWrap for bool { + /// Construct `Some(T)`, conditionally on a boolean. + fn some_if(self, T) -> Option; + /// A lazy equivalent of `some_if`. + fn lazy_if T>(self, F) -> Option; + } ``` -Similar in intention and motivation to the `Try` trait for `Result`. # Motivation [motivation]: #motivation -This addition will increase the legibility of code segments and assist in defining the thought processes and motivations of programmers through code. The use cases of this addition are problems 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 pseudocode: +This addition will increase the legibility of code segments and assist in defining the thought processes and motivations of programmers through their code. The use cases of this addition are problems which are expressable in the following Rust pseudocode: ``` - if P(x) { - Some(F(y)) + if boolean { + Some(value) } 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 fashion through their code. +The outcome of this addition will reduce repeated unexpressive code which introduces bugs during refactoring and present the thought process of the programmer in a clearer fashion through their code. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -54,58 +49,49 @@ However only the `if` branch of this code segment is the important part we're co Some(x) } ``` -But the `else` branch is required for returning `None` value if `x == 0` evaluates to false. -Fortunately Rusts `Option` type has functionality to get rid of the unecessary code: +But the `else` branch is required for returning the `None` value if `x == 0` evaluates to false. +Fortunately Rusts `bool` type has functionality to get rid of the unecessary code: ``` let x = 0; - Option::on_pred(x == 0, x) + (x == 0).some_if(x) ``` This code has the exact same behaviour as our original `if` statement. Our code is however 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()) + false.some_if(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: ``` - Option::lazy_pred(false, foo) + false.lazy_if(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. +`bool`s `lazy_if` 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 First-order 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 this RFC suggests 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)| + | x | x.some_if(y) | + | F | None | + | T | Some(y) | ``` This RFCs initial proposal for how this addition could be implemented is: ``` - impl Option { - /// A straight forward implementation of the `implies` operation for predicate logic. - fn on_pred(pred: bool, value: T) -> Self { - if pred { + impl OptionWrap for bool { + /// Wrapper around common if/else boiler plate. + fn some_if(self, value: T) -> Option { + if self { Some(value) } else { None } } - /// A lazy implementation of the `implies` operation when necessary. - fn lazy_pred(pred: bool, func: F) -> Self + /// A lazy implementation of `some_if`. + fn lazy_if(self, func: F) -> Option where F: FnOnce() -> T { - if pred { + if self { Some(func()) } else { None @@ -118,23 +104,23 @@ This implementation covers the use cases proposed in the earlier examples and an # 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. +This is 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 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. +The implementation proposed is clear, easily documented and is the minimal ammount of code 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: +- Not including the `some_if` 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) + //true.some_if(x) + true.lazy_if(|| 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 equivalent `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, 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 adequately cover syntax support for both the `on_pred` and `lazy_pred` functions in a meaningful manner and removing either one is disadvantageous as discussed above. +- Not including the `lazy_if` function. However, as discussed, this leaves the `some_if` function at a disadvantage when the equivalent `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, 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 adequately cover syntax support for both the `some_if` and `lazy_if` functions in a meaningful manner and removing either one is disadvantageous as discussed above. # Unresolved questions [unresolved]: #unresolved-questions From d30578e401e1cfef5f123f0153df2ea9596ca390 Mon Sep 17 00:00:00 2001 From: Dynisious Date: Sun, 3 Dec 2017 00:55:43 +1100 Subject: [PATCH 08/10] Missed the actual trait in the implementation --- text/0000-imply-option.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/text/0000-imply-option.md b/text/0000-imply-option.md index 69b00310001..bb324cbe843 100644 --- a/text/0000-imply-option.md +++ b/text/0000-imply-option.md @@ -79,8 +79,14 @@ The Rust addition this RFC suggests can be encapsulated as "If `x` is `true`, I ``` This RFCs initial proposal for how this addition could be implemented is: ``` - impl OptionWrap for bool { + trait OptionWrap { /// Wrapper around common if/else boiler plate. + fn some_if(self, value: T) -> Option; + /// A lazy implementation of `some_if`. + fn lazy_if(self, func: F) -> Option; + } + + impl OptionWrap for bool { fn some_if(self, value: T) -> Option { if self { Some(value) @@ -88,7 +94,6 @@ This RFCs initial proposal for how this addition could be implemented is: None } } - /// A lazy implementation of `some_if`. fn lazy_if(self, func: F) -> Option where F: FnOnce() -> T { if self { From c87c398e51985c8c06def17ecb9e29e782d42126 Mon Sep 17 00:00:00 2001 From: Dynisious Date: Wed, 27 Dec 2017 20:57:28 +1100 Subject: [PATCH 09/10] RFC Legal Double Reference Added the Legal Double Reference RFC. --- text/0000-legal-double-reference.md | 88 +++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 text/0000-legal-double-reference.md diff --git a/text/0000-legal-double-reference.md b/text/0000-legal-double-reference.md new file mode 100644 index 00000000000..67a1adb0252 --- /dev/null +++ b/text/0000-legal-double-reference.md @@ -0,0 +1,88 @@ + +- Feature Name: Legal Double Reference +- Start Date: 2017-12-27 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +The Rust borrow checker does not allow you to make an immutable reference to something while a mutable reference exists. The reasons for this are made clear, however its implementation is too strict; I propose that an immutable reference to something should be allowed to be created while there is an existing mutable reference if it is dropped before the next use of the mutable reference. +This change will mean that getting useful values from types while mutating them will be less difficult and encourage functional programming in Rust while preserving its safety. + +# Motivation +[motivation]: #motivation + +This change should be made because it encourages use of existing functional methods in Rust which can sometimes be difficult to use. +The use cases of this change are times when a value is being mutated but during that time some method needs to be called which requires an immutable reference to the value. +```rust +let mut a = vec![0u32; 3]; + +a.iter_mut() +.for_each(|b| { + //Do stuff with b. + let c = a.len(); //E0502 + //Do more stuff with b. +}); +``` +By making this change functional programming in Rust will be easier to do and unnesting will be less necessery to write Rust code. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means: + +In `Safe` Rust code it is true that between uses of a mutable reference `&mut _` the value referenced will not change i.e. +```rust +let mut a = vec![0u32; 3]; +let b = &mut a; +//Do stuff with b. +let c = a.len(); //Uses immutable reference which is created an dropped between uses + //of the mutable reference to `a`. `a` is guarenteed not to change in this time. +//Do more stuff with b. +``` +However the Rust compiler does not allow this because there is an existing mutable reference to `a` created by the call `.iter_mut()`. This change does not invalidate Rust's borrow safety but it will make references and all types with pure functions easier to use in Rust which will make writing Rust code more straight forward especially for users new to Rust since the programmer will not have to work around the borrow checker when writing safe logic similar to the example above. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This change only impacts the Rust borrow checker and should have no impact on other features of the language. +An implementation this feature, as best as I can determine, would be as such: +1. The `borrow checker` identifies the creation of an immutable reference while there is an existing mutable reference as it already does. +2. The `borrow checker` confirms that the immutable reference is dropped before the next use of the mutable reference (in the case of a function, this may require extra logic to confirm that the mutable reference is also not a parameter of the function). +3. The `borrow checker` throws the existing `E0502` if the aformentioned check fails. + +At this time I do not believe there are any corner cases in this proposed implementation. + +# Drawbacks +[drawbacks]: #drawbacks + +The absence of this feature in the language does not make logic such as the above example impossible, mearly requiring that the programmer unnests the references so that the immutable reference is used and dropped before the mutable one is created i.e. +```rust +let mut a = vec![0usize]; + +//a.push(a.len()); //Does not compile. + +let b = a.len(); +a.push(b); +``` + +# Rationale and alternatives +[alternatives]: #alternatives + +The current unnesting required to write the kind of logic shown in this RFC requires the programmer to do one of: +* Know what values they will need before actually writing the code. +* Keep Rust's borrow checker in mind while writing their code. +* Go back to create a new unnested value each time they need to create a new immutable reference while there is an existing mutable reference. + +Each of these options either require the programmer to hold the borrow checker in mind or break their workflow to work around the borrow checker. +Another alternative would be to remove the Rust borrow checker but this would go against the principals of Rust. +The proposed change preserves Rust's safety and principals and prevents the programmer having to work around the borrow checker. +Not making this change means that the programmers workflow will often be interupted while writing Rust to unnest their calls and programmers new to Rust will have to encounter the strange and unituitive E0502 error while writing code that it is reasonable to expect to work in Safe Rust. + +# Unresolved questions +[unresolved]: #unresolved-questions + +During the RFC process I expect: +* The specific implementation of this change to be discussed. +* Any unidentified edge cases of this change to be identified and discussed. From d9c9a08f4dc1427995e26db895b6a75276eef1db Mon Sep 17 00:00:00 2001 From: Dynisious Date: Wed, 27 Dec 2017 21:08:56 +1100 Subject: [PATCH 10/10] Delete 0000-legal-double-reference.md --- text/0000-legal-double-reference.md | 88 ----------------------------- 1 file changed, 88 deletions(-) delete mode 100644 text/0000-legal-double-reference.md diff --git a/text/0000-legal-double-reference.md b/text/0000-legal-double-reference.md deleted file mode 100644 index 67a1adb0252..00000000000 --- a/text/0000-legal-double-reference.md +++ /dev/null @@ -1,88 +0,0 @@ - -- Feature Name: Legal Double Reference -- Start Date: 2017-12-27 -- RFC PR: (leave this empty) -- Rust Issue: (leave this empty) - -# Summary -[summary]: #summary - -The Rust borrow checker does not allow you to make an immutable reference to something while a mutable reference exists. The reasons for this are made clear, however its implementation is too strict; I propose that an immutable reference to something should be allowed to be created while there is an existing mutable reference if it is dropped before the next use of the mutable reference. -This change will mean that getting useful values from types while mutating them will be less difficult and encourage functional programming in Rust while preserving its safety. - -# Motivation -[motivation]: #motivation - -This change should be made because it encourages use of existing functional methods in Rust which can sometimes be difficult to use. -The use cases of this change are times when a value is being mutated but during that time some method needs to be called which requires an immutable reference to the value. -```rust -let mut a = vec![0u32; 3]; - -a.iter_mut() -.for_each(|b| { - //Do stuff with b. - let c = a.len(); //E0502 - //Do more stuff with b. -}); -``` -By making this change functional programming in Rust will be easier to do and unnesting will be less necessery to write Rust code. - -# Guide-level explanation -[guide-level-explanation]: #guide-level-explanation - -Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means: - -In `Safe` Rust code it is true that between uses of a mutable reference `&mut _` the value referenced will not change i.e. -```rust -let mut a = vec![0u32; 3]; -let b = &mut a; -//Do stuff with b. -let c = a.len(); //Uses immutable reference which is created an dropped between uses - //of the mutable reference to `a`. `a` is guarenteed not to change in this time. -//Do more stuff with b. -``` -However the Rust compiler does not allow this because there is an existing mutable reference to `a` created by the call `.iter_mut()`. This change does not invalidate Rust's borrow safety but it will make references and all types with pure functions easier to use in Rust which will make writing Rust code more straight forward especially for users new to Rust since the programmer will not have to work around the borrow checker when writing safe logic similar to the example above. - -# Reference-level explanation -[reference-level-explanation]: #reference-level-explanation - -This change only impacts the Rust borrow checker and should have no impact on other features of the language. -An implementation this feature, as best as I can determine, would be as such: -1. The `borrow checker` identifies the creation of an immutable reference while there is an existing mutable reference as it already does. -2. The `borrow checker` confirms that the immutable reference is dropped before the next use of the mutable reference (in the case of a function, this may require extra logic to confirm that the mutable reference is also not a parameter of the function). -3. The `borrow checker` throws the existing `E0502` if the aformentioned check fails. - -At this time I do not believe there are any corner cases in this proposed implementation. - -# Drawbacks -[drawbacks]: #drawbacks - -The absence of this feature in the language does not make logic such as the above example impossible, mearly requiring that the programmer unnests the references so that the immutable reference is used and dropped before the mutable one is created i.e. -```rust -let mut a = vec![0usize]; - -//a.push(a.len()); //Does not compile. - -let b = a.len(); -a.push(b); -``` - -# Rationale and alternatives -[alternatives]: #alternatives - -The current unnesting required to write the kind of logic shown in this RFC requires the programmer to do one of: -* Know what values they will need before actually writing the code. -* Keep Rust's borrow checker in mind while writing their code. -* Go back to create a new unnested value each time they need to create a new immutable reference while there is an existing mutable reference. - -Each of these options either require the programmer to hold the borrow checker in mind or break their workflow to work around the borrow checker. -Another alternative would be to remove the Rust borrow checker but this would go against the principals of Rust. -The proposed change preserves Rust's safety and principals and prevents the programmer having to work around the borrow checker. -Not making this change means that the programmers workflow will often be interupted while writing Rust to unnest their calls and programmers new to Rust will have to encounter the strange and unituitive E0502 error while writing code that it is reasonable to expect to work in Safe Rust. - -# Unresolved questions -[unresolved]: #unresolved-questions - -During the RFC process I expect: -* The specific implementation of this change to be discussed. -* Any unidentified edge cases of this change to be identified and discussed.