Piping e Estilo Funcional
Soyuz é multiparadigma — você pode escrever código orientado a objetos, funcional, ou misturar os dois. Os operadores de piping tornam o encadeamento de transformações natural e legível.
O operador |>
Section titled “O operador |>”|> pega o resultado da expressão à esquerda e passa como primeiro argumento para a função à direita:
// Sem pipeval resultado = transformar(validar(buscar(1)))
// Com pipe — lê na ordem de execuçãoval resultado = buscar(1) |> validar |> transformarPipe com valores simples
Section titled “Pipe com valores simples”val resultado = [1, 2, 3, 4, 5] |> filter(fn(x) => x > 2) |> map(fn(x) => x * 2) |> reduce(fn(acc, x) => acc + x, 0)Com aplicação parcial, você pode tornar isso ainda mais curto:
val multiplicar = fn(x, y) => x * yval dobrar = multiplicar(2, _)
val r = [1, 2, 3] |> map(dobrar)PipeQuest |?> (Result & Option aware)
Section titled “PipeQuest |?> (Result & Option aware)”O operador PipeQuest (|?>) é usado para encadear operações que podem falhar ou estar ausentes. Ele curto-circuita automaticamente se encontrar um erro ou um valor nulo.
Se você tem um Option[T] ou Result[T] à esquerda:
- Sucesso (
Some/Ok): Extrai o valor interno e o passa para a próxima função. - Falha (
None/Err): Para a execução do pipeline e propaga a falha.
fn buscar(id: Int) -> Result[Usuario]fn validar(u: Usuario) -> Result[Usuario]
// Se buscar retornar Err, validar nunca será executada.val resultado = buscar(1) |?> validarIsso torna a integração entre Option e Result extremamente fluida e segura.
Map, Filter, Reduce
Section titled “Map, Filter, Reduce”As três operações fundamentais de transformação de listas:
map — transforma cada elemento
Section titled “map — transforma cada elemento”val dobrados = [1, 2, 3].map(fn(x: Int) => x * 2)filter — seleciona elementos
Section titled “filter — seleciona elementos”val ativos = usuarios.filter(fn(u: Usuario) => u.ativo)reduce — agrega em um único valor
Section titled “reduce — agrega em um único valor”val soma = [1, 2, 3, 4, 5].reduce(fn(acc: Int, x: Int) => acc + x, 0)Funções como valores
Section titled “Funções como valores”Qualquer função pode ser passada como valor:
fn dobrar(x: Int) -> Int = x * 2val dobrados = [1, 2, 3].map(dobrar)Imutabilidade em prática
Section titled “Imutabilidade em prática”A combinação de val, records imutáveis e funções puras facilita raciocinar sobre o código:
fn aplicarDesconto(produto: Produto, percentual: Float) -> Produto { // Retorna um NOVO record — não muta o original return Produto { id: produto.id, nome: produto.nome, preco: produto.preco * (1.0 - percentual / 100.0) }}Quando usar pipe vs métodos
Section titled “Quando usar pipe vs métodos”Use pipe quando:
- Está transformando dados por várias etapas independentes.
- As funções vêm de módulos diferentes.
Use métodos quando:
- A operação é intrinsecamente parte do objeto ou da coleção.
- O encadeamento é natural ao tipo (
.map,.filterem listas).
// Pipe — transformação de fluxoval resultado = dadosBrutos |?> parsear |?> validar |?> salvar
// Métodos — operações sobre coleçãoval nomes = usuarios .filter(fn(u) => u.ativo) .map(fn(u) => u.nome)