Pipes
A tradução direta de pipes
é canos
. Você pode pensar sobreCanos
como os canos de sua casa. Eles funcionam para passar água de um lugar para outro. No meio dessa passagem podemos por algumas coisas como, válvulas e filtros, até chegar em nossa torneira pronta para uso.
A água seria nossos dados. As válvulas e filtros os redutores e a torneira seria nosso transformador. A ideia aqui é passar dados a fim de realizar operações sequenciais para termos uma resposta final que precisamos. dados -> função 1 -> função 2 -> função N -> dados transformados pronto para uso.
Normalmente utilizamos funções dessa forma
funcao(argumento1)
Com pipe, é um pouco diferente.
argumento_1
|> funcao()
A troca da sequência, onde colocamos o parâmetro antes da função nos possibilita encadear várias funções.
arumento_1
|> funcao_1()
|> funcao_2()
|> funcao_3()
No exemplo acima, a funcao_1
recebe o argumento_1,
a funcao_2
recebe a resposta da funcao_1
e a funcao_3
recebe a resposta da funcao_2
.
Podemos utilizar pipes
Com funções nativas;
Em um linha só;
iex(1)> "Iago Effting" |> String.downcase() |> IO.inspect()
"iago effting"
Com condicionais (mesmo não sendo tão legal de ler)
iex(1)> 100 \
...(1)> |> case do
...(1)> 100 -> "It's 100"
...(1)> _ -> "it's something else"
...(1)> end
"It's 100"
Podemos utilizar basicamente qualquer função no pipe, seguindo a regra do primeiro parâmetro vir antes.
Exemplo
Vamos fazer algo então. Precisamos fazer uma sequencia de operações para atender a uma regra.
Dado um valor vamos
Somar o valor de entrada por 10;
Multiplicar o valor por 2;
Diminuir por 5
Vamos começar sem a utilização de pipes
. Para isso adicionarei um parâmetro extra em nossa função, onde poderemos colocar e cair na função que não utiliza pipes ou apenas passando um valor e cairá na função com pipe
. Vamos sem pipes
primeiro.
defmodule CalculatorTest do
use ExUnit.Case
test "Not using pipes" do
value = 5
assert Calculator.crazy_rule(value, :without_pipe) == 25
end
end
mix test test/calculator_test.exs
warning: Calculator.crazy_rule/1 is undefined (module Calculation is not available or is yet to be defined)
test/pipes_test.exs:7: PipesTest."test Not using pipes"/1
1) test Not using pipes (PipesTest)
test/pipes_test.exs:4
** (UndefinedFunctionError) function Calculator.crazy_rule/1 is undefined (module Calculation is not available)
code: assert Calculator.crazy_rule(value) == 25
stacktrace:
Calculator.crazy_rule(5)
test/calculator_test.exs:7: (test)
Finished in 0.04 seconds (0.00s async, 0.04s sync)
1 test, 1 failure
Não temos criado o modulo, vamos cria-lo.
defmodule Calculator do
def crazy_rule(value, :not_using_pipes) do
end
end
mix test test/calculator_test.exs
1) test Not using pipes (PipesTest)
test/calculator_test.exs:4
Assertion with == failed
code: assert Calculator.crazy_rule(value) == 25
left: nil
right: 25
stacktrace:
test/calculator_test.exs:7: (test)
Finished in 0.02 seconds (0.00s async, 0.02s sync)
1 test, 1 failure
Agora podemos seguir.
Precisamos resolver esse modulo usando funções e elas ficam bem claras quais são. Basta pegar os passos que devem ser feitos somar
, diminuir
e multiplicar
.
Podemos resolver isso, assim:
defmodule Calculator do
def crazy_rule(value, :without_pipes) do
value = sum(value, 10)
value = multiple(value, 2)
value = decrease(value, 5)
value # Retorna o valor final
end
defp sum(current_value, value), do: current + value
defp multiple(current_value, value), do: current_value * value
defp decrease(current_value, value), do: current_value - value
end
Criamos as funções referente a regra e juntamos tudo na função publica crazy_rule
. Vamos rodar isso.
mix test test/calculator_test.exs
Compiling 1 file (.ex)
.
Finished in 0.01 seconds (0.00s async, 0.01s sync)
1 test, 0 failures
Tudo funcionando bem e nosso teste passando.
Vamos agora criar utilizando pipes.
defmodule PipesTest do
use ExUnit.Case
# ...
test "Using pipes" do
value = 5
assert Calculator.crazy_rule(value, :with_pipes) == 25
end
end
Adicionando a função utilizando pipes
:
defmodule Calculator do
def crazy_rule(value, :without_pipes) do
value = sum(value, 10)
value = multiple(value, 2)
value = decrease(value, 5)
value # Retorna o valor
end
def crazy_rule(value, :with_pipes) do
value
|> sum(10)
|> multiple(2)
|> decrease(5)
end
defp sum(current_value, value), do: current_value + value
defp multiple(current_value, value), do: current_value * value
defp decrease(current_value, value), do: current_value - value
end
Execute os testes novamente
mix test test/calculator_test.exs
..
Finished in 0.01 seconds (0.00s async, 0.01s sync)
2 tests, 0 failures
A utilização de pipes deixa o código mais elegante a fácil de entender. A leitura se torna mais fluída e colocar uma nova função no meio dele fica bem simples, enquanto sem pipes se torna repetitiva e verbosa.
Pipes
As funções no pipe deve seguir uma ordem lógica. Quando a ordem é alterada, o resultado tende a se alterar também. Existem formas de programar pipes, onde isso não será um problema, mas em nosso exemplo, é. Tenha isso em mente.
Conclusão
A utilização pipes se torna simples em colocar funções em uma sequência lógica para atender um requisito, mesmo que estranho. É amplamente utilizado no elixir e recomendado. Devemos sempre seguir a regra de, o primeiro argumento deve vir antes da chamada da função
argumento_1
|> funcao(argumento_2) # funcao/2
|> outra(argumento_2) # outra/1
Pense bem ao utilizar pipes, podem ser uma boa forma de melhorar a legibilidade de seu código.
Last updated
Was this helpful?