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.