-
Notifications
You must be signed in to change notification settings - Fork 248
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add
Option<T>
to noir stdlib (#1781)
* Add Option * Fix path * Add option test * Move test * Add docs and filter, flatten methods * Fix stdlib
- Loading branch information
Showing
5 changed files
with
218 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[package] | ||
name = "option" | ||
authors = [""] | ||
compiler_version = "0.7.0" | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use dep::std::option::Option; | ||
|
||
fn main() { | ||
let none = Option::none(); | ||
let some = Option::some(3); | ||
|
||
assert(none.is_none()); | ||
assert(some.is_some()); | ||
|
||
assert(some.unwrap() == 3); | ||
|
||
assert(none.unwrap_or(2) == 2); | ||
assert(some.unwrap_or(2) == 3); | ||
|
||
assert(none.unwrap_or_else(|| 5) == 5); | ||
assert(some.unwrap_or_else(|| 5) == 3); | ||
|
||
assert(none.map(|x| x * 2).is_none()); | ||
assert(some.map(|x| x * 2).unwrap() == 6); | ||
|
||
assert(none.map_or(0, |x| x * 2) == 0); | ||
assert(some.map_or(0, |x| x * 2) == 6); | ||
|
||
assert(none.map_or_else(|| 0, |x| x * 2) == 0); | ||
assert(some.map_or_else(|| 0, |x| x * 2) == 6); | ||
|
||
assert(none.and(none).is_none()); | ||
assert(none.and(some).is_none()); | ||
assert(some.and(none).is_none()); | ||
assert(some.and(some).is_some()); | ||
|
||
let add1_u64 = |value: Field| Option::some(value as u64 + 1); | ||
|
||
assert(none.and_then(|_value| Option::none()).is_none()); | ||
assert(none.and_then(add1_u64).is_none()); | ||
assert(some.and_then(|_value| Option::none()).is_none()); | ||
assert(some.and_then(add1_u64).unwrap() == 4); | ||
|
||
assert(none.or(none).is_none()); | ||
assert(none.or(some).is_some()); | ||
assert(some.or(none).is_some()); | ||
assert(some.or(some).is_some()); | ||
|
||
assert(none.or_else(|| Option::none()).is_none()); | ||
assert(none.or_else(|| Option::some(5)).is_some()); | ||
assert(some.or_else(|| Option::none()).is_some()); | ||
assert(some.or_else(|| Option::some(5)).is_some()); | ||
|
||
assert(none.xor(none).is_none()); | ||
assert(none.xor(some).is_some()); | ||
assert(some.xor(none).is_some()); | ||
assert(some.xor(some).is_none()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
struct Option<T> { | ||
_is_some: bool, | ||
value: T, | ||
} | ||
|
||
impl<T> Option<T> { | ||
/// Constructs a None value | ||
fn none() -> Self { | ||
Self { _is_some: false, value: crate::unsafe::zeroed() } | ||
} | ||
|
||
/// Constructs a Some wrapper around the given value | ||
fn some(value: T) -> Self { | ||
Self { _is_some: true, value } | ||
} | ||
|
||
/// True if this Option is None | ||
fn is_none(self) -> bool { | ||
!self._is_some | ||
} | ||
|
||
/// True if this Option is Some | ||
fn is_some(self) -> bool { | ||
self._is_some | ||
} | ||
|
||
/// Asserts `self.is_some()` and returns the wrapped value. | ||
fn unwrap(self) -> T { | ||
assert(self._is_some); | ||
self.value | ||
} | ||
|
||
/// Returns the wrapped value if `self.is_some()`. Otherwise, returns the given default value. | ||
fn unwrap_or(self, default: T) -> T { | ||
if self._is_some { | ||
self.value | ||
} else { | ||
default | ||
} | ||
} | ||
|
||
/// Returns the wrapped value if `self.is_some()`. Otherwise, calls the given function to return | ||
/// a default value. | ||
fn unwrap_or_else(self, default: fn() -> T) -> T { | ||
if self._is_some { | ||
self.value | ||
} else { | ||
default() | ||
} | ||
} | ||
|
||
/// If self is `Some(x)`, this returns `Some(f(x))`. Otherwise, this returns `None`. | ||
fn map<U>(self, f: fn(T) -> U) -> Option<U> { | ||
if self._is_some { | ||
Option::some(f(self.value)) | ||
} else { | ||
Option::none() | ||
} | ||
} | ||
|
||
/// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns the given default value. | ||
fn map_or<U>(self, default: U, f: fn(T) -> U) -> U { | ||
if self._is_some { | ||
f(self.value) | ||
} else { | ||
default | ||
} | ||
} | ||
|
||
/// If self is `Some(x)`, this returns `f(x)`. Otherwise, this returns `default()`. | ||
fn map_or_else<U>(self, default: fn() -> U, f: fn(T) -> U) -> U { | ||
if self._is_some { | ||
f(self.value) | ||
} else { | ||
default() | ||
} | ||
} | ||
|
||
/// Returns None if self is None. Otherwise, this returns `other`. | ||
fn and(self, other: Self) -> Self { | ||
if self.is_none() { | ||
Option::none() | ||
} else { | ||
other | ||
} | ||
} | ||
|
||
/// If self is None, this returns None. Otherwise, this calls the given function | ||
/// with the Some value contained within self, and returns the result of that call. | ||
/// | ||
/// In some languages this function is called `flat_map` or `bind`. | ||
fn and_then<U>(self, f: fn(T) -> Option<U>) -> Option<U> { | ||
if self._is_some { | ||
f(self.value) | ||
} else { | ||
Option::none() | ||
} | ||
} | ||
|
||
/// If self is Some, return self. Otherwise, return `other`. | ||
fn or(self, other: Self) -> Self { | ||
if self._is_some { | ||
self | ||
} else { | ||
other | ||
} | ||
} | ||
|
||
/// If self is Some, return self. Otherwise, return `default()`. | ||
fn or_else<U>(self, default: fn() -> Self) -> Self { | ||
if self._is_some { | ||
self | ||
} else { | ||
default() | ||
} | ||
} | ||
|
||
// If only one of the two Options is Some, return that option. | ||
// Otherwise, if both options are Some or both are None, None is returned. | ||
fn xor(self, other: Self) -> Self { | ||
if self._is_some { | ||
if other._is_some { | ||
Option::none() | ||
} else { | ||
self | ||
} | ||
} else if other._is_some { | ||
other | ||
} else { | ||
Option::none() | ||
} | ||
} | ||
|
||
/// Returns `Some(x)` if self is `Some(x)` and `predicate(x)` is true. | ||
/// Otherwise, this returns `None` | ||
fn filter(self, predicate: fn(T) -> bool) -> Self { | ||
if self._is_some { | ||
if predicate(self.value) { | ||
self | ||
} else { | ||
Option::none() | ||
} | ||
} else { | ||
Option::none() | ||
} | ||
} | ||
|
||
/// Flattens an Option<Option<T>> into a Option<T>. | ||
/// This returns None if the outer Option is None. Otherwise, this returns the inner Option. | ||
fn flatten(option: Option<Option<T>>) -> Option<T> { | ||
if option._is_some { | ||
option.value | ||
} else { | ||
Option::none() | ||
} | ||
} | ||
} |