Skip to main content

Piping values into expressions

It can sometimes be hard to read deeply nested expressions involving several function applications.

import Iter "mo:base/Iter";
import List "mo:base/List";

{ multiples =
List.filter<Nat>(
Iter.toList(Iter.range(0, 10)),
func n { n % 3 == 0 }) };

This expression take the range of numbers 0..10, converts it to a list, filters the list for multiples of three and returns a record containing the result.

To make such expressions more readable, you can use Motoko's pipe operator <exp1> |> <exp2>. The operator evaluates the first argument <exp1>, and lets you refer to its value in <exp2> using the special placeholder expression _.

Using this, you can write the former expression as:

import Iter "mo:base/Iter";
import List "mo:base/List";

Iter.range(0, 10) |>
Iter.toList _ |>
List.filter<Nat>(_, func n { n % 3 == 0 }) |>
{ multiples = _ };

Now, the textual order of operations corresponds to our English explanation above: "this expression takes the range of numbers 0..10, converts it to a list, filters the list for multiples of three and returns a record containing the result".

The pipe expression <exp1> |> <exp2> is just syntactic sugar for the following block binding <exp1> to a reserved placeholder identifier, p, before returning <exp2>:

do { let p = <exp1>; <exp2> }

The otherwise inaccessible placeholder identifier p can only referenced by the placeholder expression _. Multiple references to _ are allowed and refer to the same value within the same pipe operation.

Note that using _ as an expression outside of a pipe operation, where it is undefined, is an error.

For example:

let x = _;

produces the compile-time error "type error [M0057], unbound variable _".

(Internally, the compiler uses the reserved identifier _ as the name for the placeholder called p above, so this let is just referencing an undefined variable).

See here for more details.