Skip to content
This repository has been archived by the owner on Oct 17, 2023. It is now read-only.

Add docs for lambdas & closures #356

Merged
merged 3 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 24 additions & 0 deletions docs/language_concepts/00_data_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,3 +403,27 @@ fn multiplyBy2(x: &mut Field) {
*x = *x * 2;
}
```

## Function Types

Noir supports higher-order functions. The syntax for a function type is as follows:

```rust
fn(arg1_type, arg2_type, ...) -> return_type
```

Example:

```rust
fn assert_returns_100(f: fn() -> Field) { // f takes no args and returns a Field
assert(f() == 100);
}

fn main() {
assert_returns_100(|| 100); // ok
assert_returns_100(|| 150); // fails
}
```

A function type also has an optional capture environment - this is necessary to support closures.
See [Lambdas](./08_lambdas.md) for more details.
11 changes: 11 additions & 0 deletions docs/language_concepts/01_functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,14 @@ follows:
```rust
assert(MyStruct::sum(s) == 42);
```

## Lambdas

Lambdas are anonymous functions. They follow the syntax of Rust - `|arg1, arg2, ..., argN| return_expression`.

```rust
let add_50 = |val| val + 50;
assert(add_50(100) == 150);
```

See [Lambdas](./08_lambdas.md) for more details.
80 changes: 80 additions & 0 deletions docs/language_concepts/08_lambdas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: Lambdas
description: Learn how to use anonymous functions in Noir programming language.
keywords: [Noir programming language, lambda, closure, function, anonymous function]
---

## Introduction

Lambdas are anonymous functions. The syntax is `|arg1, arg2, ..., argN| return_expression`.

```rust
let add_50 = |val| val + 50;
assert(add_50(100) == 150);
```

A block can be used as the body of a lambda, allowing you to declare local variables inside it:

```rust
let cool = || {
let x = 100;
let y = 100;
x + y
}

assert(cool() == 200);
```

## Closures

Inside the body of a lambda, you can use variables defined in the enclosing function. Such lambdas are called **closures**. In this example `x` is defined inside `main` and is accessed from within the lambda:

```rust
fn main() {
let x = 100;
let closure = || x + 150;
assert(closure() == 150);
}
```

## Passing closures to higher-order functions

It may catch you by surprise that the following code fails to compile:

```rust
fn foo(f: fn () -> Field) -> Field {
f()
}

fn main() {
let (x, y) = (50, 50);
assert(foo(|| x + y) == 100); // error :(
}
```

The reason is that the closure's capture environment affects its type - we have a closure that captures two Fields and `foo`
critesjosh marked this conversation as resolved.
Show resolved Hide resolved
expects a regular function as an argument - those are incompatible.
:::note

alexvitkov marked this conversation as resolved.
Show resolved Hide resolved
Variables contained within the `||` are the closure's parameters, and the expression that follows it is the closure's body. The capture environment is comprised of any variables used in the closure's body that are not parameters.

E.g. in |x| x + y, y would be a captured variable, but x would not be, since it is a parameter of the closure.

:::
The syntax for the type of a closure is `fn[env](args) -> ret_type`, where `env` is the capture environment of the closure -
in this example that's `(Field, Field)`.

The best solution in our case is to make `foo` generic over the environment type of its parameter, so that it can be called
with closures with any environment, as well as with regular functions:

```rust
fn foo<Env>(f: fn[Env]() -> Field) -> Field {
f()
}

fn main() {
let (x, y) = (50, 50);
assert(foo(|| x + y) == 100); // compiles fine
assert(foo(|| 60) == 60); // compiles fine
}
```