Slide 21 de 74

Pipes

Uma sequência se lê na ordem em que roda
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(", ")

	// A mesma chamada, e no que a linha acima compila.
	echo join(map(filter(nums, isEven), label), ", ")
}
A quem ele se liga
func double(n: Int): Int { return n * 2 }

proc main(): void {
	Str[dyn] words = ["a", "b", "c"]

	// Mais forte que uma comparação, então isto é len(words) > 2.
	echo words | len > 2

	// E ele pega o valor ao lado: 1 + double(2), e não double(3).
	echo 1 + 2 | double
	echo (1 + 2) | double
	echo 2 ** 3 | double    // 16 — ** liga mais forte que um pipe
}

x | f(a) é açúcar sintático para f(x, a): o valor da esquerda vira o primeiro argumento da chamada, então uma sequência deles se lê na ordem em que roda, e não de dentro para fora. O | é associativo à esquerda, e um estágio que não tem mais nada dispensa os parênteses — x | f é f(x), assim como x | f() é.

Ele se liga mais forte que + - * / % e que toda comparação, e mais fraco que os operadores prefixados e que **, o que permite um pipeline dos dois lados de um deles: v | len > 2 compara o comprimento, e 2 ** 3 | f é f(8). Ele pega o valor escrito ao lado dele, então 1 + 2 | f é 1 + f(2); um valor calculado vai entre parênteses.

Um proc, uma função nativa, uma chamada da biblioteca e um construtor entram num pipe do mesmo jeito. O que um pipe deixa para trás é exatamente a chamada que alguém poderia ter escrito à mão — não existe objeto de pipeline, e nada fica adiado.