Skip to content

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.


|> pega o resultado da expressão à esquerda e passa como primeiro argumento para a função à direita:

// Sem pipe
val resultado = transformar(validar(buscar(1)))
// Com pipe — lê na ordem de execução
val resultado = buscar(1) |> validar |> transformar

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 * y
val dobrar = multiplicar(2, _)
val r = [1, 2, 3] |> map(dobrar)

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:

  1. Sucesso (Some/Ok): Extrai o valor interno e o passa para a próxima função.
  2. 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) |?> validar

Isso torna a integração entre Option e Result extremamente fluida e segura.


As três operações fundamentais de transformação de listas:

val dobrados = [1, 2, 3].map(fn(x: Int) => x * 2)
val ativos = usuarios.filter(fn(u: Usuario) => u.ativo)
val soma = [1, 2, 3, 4, 5].reduce(fn(acc: Int, x: Int) => acc + x, 0)

Qualquer função pode ser passada como valor:

fn dobrar(x: Int) -> Int = x * 2
val dobrados = [1, 2, 3].map(dobrar)

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)
}
}

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, .filter em listas).
// Pipe — transformação de fluxo
val resultado = dadosBrutos |?> parsear |?> validar |?> salvar
// Métodos — operações sobre coleção
val nomes = usuarios
.filter(fn(u) => u.ativo)
.map(fn(u) => u.nome)