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

feat: Add more slice methods to the stdlib #5424

Merged
merged 6 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions compiler/noirc_frontend/src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

use chumsky::prelude::*;
use iter_extended::vecmap;
use lalrpop_util::lalrpop_mod;

Check warning on line 51 in compiler/noirc_frontend/src/parser/parser.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lalrpop)

Check warning on line 51 in compiler/noirc_frontend/src/parser/parser.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lalrpop)
use noirc_errors::{Span, Spanned};

mod assertion;
Expand All @@ -62,8 +62,8 @@
mod traits;
mod types;

// synthesized by LALRPOP

Check warning on line 65 in compiler/noirc_frontend/src/parser/parser.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (LALRPOP)
lalrpop_mod!(pub noir_parser);

Check warning on line 66 in compiler/noirc_frontend/src/parser/parser.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lalrpop)

#[cfg(test)]
mod test_helpers;
Expand All @@ -87,12 +87,12 @@

if cfg!(feature = "experimental_parser") {
for parsed_item in &parsed_module.items {
if lalrpop_parser_supports_kind(&parsed_item.kind) {

Check warning on line 90 in compiler/noirc_frontend/src/parser/parser.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lalrpop)
match &parsed_item.kind {
ItemKind::Import(parsed_use_tree) => {
prototype_parse_use_tree(Some(parsed_use_tree), source_program);
}
// other kinds prevented by lalrpop_parser_supports_kind

Check warning on line 95 in compiler/noirc_frontend/src/parser/parser.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (lalrpop)
_ => unreachable!(),
}
}
Expand All @@ -109,7 +109,7 @@
}

let mut lexer = Lexer::new(input);
lexer = lexer.skip_whitespaces(false);

Check warning on line 112 in compiler/noirc_frontend/src/parser/parser.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (whitespaces)
let mut errors = Vec::new();

// NOTE: this is a hack to get the references working
Expand Down Expand Up @@ -1117,16 +1117,11 @@
}

fn quote() -> impl NoirParser<ExpressionKind> {
token_kind(TokenKind::Quote).validate(|token, span, emit| {
let tokens = match token {
token_kind(TokenKind::Quote).map(|token| {
ExpressionKind::Quote(match token {
Token::Quote(tokens) => tokens,
_ => unreachable!("token_kind(Quote) should guarantee parsing only a quote token"),
};
emit(ParserError::with_reason(
ParserErrorReason::ExperimentalFeature("quoted expressions"),
span,
));
ExpressionKind::Quote(tokens)
})
})
}

Expand Down
70 changes: 65 additions & 5 deletions docs/docs/noir/concepts/data_types/slices.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ fn main() {
Applies a function to each element of the slice, returning a new slice containing the mapped elements.

```rust
fn map<U>(self, f: fn(T) -> U) -> [U]
fn map<U, Env>(self, f: fn[Env](T) -> U) -> [U]
```

example
Expand All @@ -213,7 +213,7 @@ Applies a function to each element of the slice, returning the final accumulated
parameter is the initial value.

```rust
fn fold<U>(self, mut accumulator: U, f: fn(U, T) -> U) -> U
fn fold<U, Env>(self, mut accumulator: U, f: fn[Env](U, T) -> U) -> U
```

This is a left fold, so the given function will be applied to the accumulator and first element of
Expand Down Expand Up @@ -247,7 +247,7 @@ fn main() {
Same as fold, but uses the first element as the starting element.

```rust
fn reduce(self, f: fn(T, T) -> T) -> T
fn reduce<Env>(self, f: fn[Env](T, T) -> T) -> T
```

example:
Expand All @@ -260,12 +260,72 @@ fn main() {
}
```

### filter

Returns a new slice containing only elements for which the given predicate returns true.

```rust
fn filter<Env>(self, f: fn[Env](T) -> bool) -> Self
```

example:

```rust
fn main() {
let slice = &[1, 2, 3, 4, 5];
let odds = slice.filter(|x| x % 2 == 1);
assert_eq(odds, &[1, 3, 5]);
}
```

### join

Flatten each element in the slice into one value, separated by `separator`.

Note that although slices implement `Append`, `join` cannot be used on slice
elements since nested slices are prohibited.

```rust
fn join(self, separator: T) -> T where T: Append
```

example:

```rust
struct Accumulator {
total: Field,
}

// "Append" two accumulators by adding them
impl Append for Accumulator {
fn empty() -> Self {
Self { total: 0 }
}

fn append(self, other: Self) -> Self {
Self { total: self.total + other.total }
}
}

fn main() {
let slice = &[1, 2, 3, 4, 5].map(|total| Accumulator { total });

let result = slice.join(Accumulator::empty());
assert_eq(result, Accumulator { total: 15 });

// We can use a non-empty separator to insert additional elements to sum:
let separator = Accumulator { total: 10 };
let result = slice.join(separator);
assert_eq(result, Accumulator { total: 55 });
}
```

### all

Returns true if all the elements satisfy the given predicate

```rust
fn all(self, predicate: fn(T) -> bool) -> bool
fn all<Env>(self, predicate: fn[Env](T) -> bool) -> bool
```

example:
Expand All @@ -283,7 +343,7 @@ fn main() {
Returns true if any of the elements satisfy the given predicate

```rust
fn any(self, predicate: fn(T) -> bool) -> bool
fn any<Env>(self, predicate: fn[Env](T) -> bool) -> bool
```

example:
Expand Down
38 changes: 38 additions & 0 deletions docs/docs/noir/standard_library/append.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: Append Trait
description:
The Append Trait abstracts over types that can be appended to
keywords: [append, trait]
---

`Append` can abstract over types that can be appended to - usually container types:

```rs
trait Append {
fn empty() -> Self;

fn append(self, other: Self) -> Self;
}
```

`Append` requires two methods:

- `empty`: Constructs an empty value of `Self`.
- `append`: Append two values together, returning the result.

Additionally, it is expected that for any implementation:

- `T::empty().append(x) == x`
- `x.append(T::empty()) == x`

---

## Implementations
jfecher marked this conversation as resolved.
Show resolved Hide resolved

```rs
impl<T> Append for [T]
```

```rs
impl Append for Quoted
```
33 changes: 33 additions & 0 deletions noir_stdlib/src/append.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Appends two values together, returning the result.
//
// An alternate name for this trait is `Monoid` if that is familiar.
// If not, it can be ignored.
//
// It is expected that for any implementation:
// - `T::empty().append(x) == x`
// - `x.append(T::empty()) == x`
trait Append {
fn empty() -> Self;
fn append(self, other: Self) -> Self;
}

impl<T> Append for [T] {
fn empty() -> Self {
&[]
}

fn append(self, other: Self) -> Self {
// Slices have an existing append function which this will resolve to.
self.append(other)
}
}

impl Append for Quoted {
michaeljklein marked this conversation as resolved.
Show resolved Hide resolved
fn empty() -> Self {
quote {}
}

fn append(self, other: Self) -> Self {
quote { $self $other }
}
}
1 change: 1 addition & 0 deletions noir_stdlib/src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod uint128;
mod bigint;
mod runtime;
mod meta;
mod append;

// Oracle calls are required to be wrapped in an unconstrained function
// Thus, the only argument to the `println` oracle is expected to always be an ident
Expand Down
29 changes: 29 additions & 0 deletions noir_stdlib/src/slice.nr
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::append::Append;

impl<T> [T] {
#[builtin(array_len)]
pub fn len(self) -> u32 {}
Expand Down Expand Up @@ -85,6 +87,33 @@ impl<T> [T] {
accumulator
}

// Returns a new slice containing only elements for which the given predicate
// returns true.
pub fn filter<Env>(self, predicate: fn[Env](T) -> bool) -> Self {
let mut ret = &[];
for elem in self {
if predicate(elem) {
ret = ret.push_back(elem);
}
}
ret
}

// Flatten each element in the slice into one value, separated by `separator`.
pub fn join(self, separator: T) -> T where T: Append {
let mut ret = T::empty();

if self.len() != 0 {
ret = self[0];

for i in 1 .. self.len() {
ret = ret.append(separator).append(self[i]);
}
}

ret
}

// Returns true if all elements in the slice satisfy the predicate
pub fn all<Env>(self, predicate: fn[Env](T) -> bool) -> bool {
let mut ret = true;
Expand Down
7 changes: 7 additions & 0 deletions test_programs/compile_success_empty/slice_join/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "slice_join"
type = "bin"
authors = [""]
compiler_version = ">=0.31.0"

[dependencies]
16 changes: 16 additions & 0 deletions test_programs/compile_success_empty/slice_join/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::append::Append;

fn main() {
let slice = &[1, 2, 3, 4, 5];

let odds = slice.filter(|x| x % 2 == 1);
assert_eq(odds, &[1, 3, 5]);

let odds_and_evens = append_three(odds, &[100], &[2, 4]);
assert_eq(odds_and_evens, &[1, 3, 5, 100, 2, 4]);
}

fn append_three<T>(one: T, two: T, three: T) -> T where T: Append {
// The last .append(T::empty()) should do nothing
one.append(two).append(three).append(T::empty())
jfecher marked this conversation as resolved.
Show resolved Hide resolved
}
Loading