func isEven(n: Int): Bool { return n % 2 == 0 }
func label(n: Int): Str { return "#{n}" }
proc main(): void {
Int[dyn] nums = [1, 2, 3, 4, 5, 6]
echo nums | filter(isEven) | map(label) | join(", ")
// The same call, and what the line above compiles to.
echo join(map(filter(nums, isEven), label), ", ")
}func double(n: Int): Int { return n * 2 }
proc main(): void {
Str[dyn] words = ["a", "b", "c"]
// Tighter than a comparison, so this is len(words) > 2.
echo words | len > 2
// And it takes the value beside it: 1 + double(2), not double(3).
echo 1 + 2 | double
echo (1 + 2) | double
echo 2 ** 3 | double // 16 — ** binds tighter than a pipe
}x | f(a) is sugar for f(x, a): the value on the left becomes the call's first argument, so a chain of them reads in the order it runs rather than inside out. | is left-associative, and a stage with nothing else in it needs no parentheses — x | f is f(x), as x | f() is.
It binds tighter than + - * / % and every comparison, and looser than the prefix operators and **, which lets a pipeline sit on either side of one: v | len > 2 compares the length, and 2 ** 3 | f is f(8). It takes the value written beside it, so 1 + 2 | f is 1 + f(2); a computed value goes in parentheses.
A proc, a builtin, a library call and a constructor all pipe. What a pipe leaves behind is exactly the call somebody could have written by hand — there is no pipeline object, and nothing is deferred.