---
title: "Programação Funcional em Kotlin: Guia Completo em Português | Kotlin Brasil"
url: "https://kotlin.dev.br/guias/kotlin-funcional/"
markdown_url: "https://kotlin.dev.br/guias/kotlin-funcional.MD"
description: "Domine programação funcional em Kotlin: funções de alta ordem, lambdas, imutabilidade, composição de funções, tail recursion e Arrow library."
date: "2026-03-17"
author: "Karina Melo"
---

# Programação Funcional em Kotlin: Guia Completo em Português | Kotlin Brasil

Domine programação funcional em Kotlin: funções de alta ordem, lambdas, imutabilidade, composição de funções, tail recursion e Arrow library.


Kotlin nasceu como uma linguagem multiparadigma, é um dos seus grandes trunfos é o suporte robusto a programação funcional sem abrir mao da orientacao a objetos. Se você vem do Java, vai perceber que Kotlin torna o estilo funcional muito mais acessivel e expressivo. Neste guia, vamos explorar cada conceito fundamental da programação funcional em Kotlin, com exemplos práticos que você pode aplicar no dia a dia dos seus projetos.

## Funções como Cidadas de Primeira Classe

Em Kotlin, funções são cidadas de primeira classe. Isso significa que você pode armazenar funções em variaveis, passa-las como argumentos para outras funções e retorna-las como resultado. Esse conceito é a base de tudo que vamos ver ao longo deste guia.

```kotlin
val saudacao: (String) -> String = { nome -> "Ola, $nome!" }

fun main() {
    println(saudacao("Kotlin Brasil"))
    // Saida: Ola, Kotlin Brasil!

    val funcoes = listOf<(Int) -> Int>(
        { it * 2 },
        { it + 10 },
        { it * it }
    )

    funcoes.forEach { f -> println(f(5)) }
    // Saida: 10, 15, 25
}
```

Repare que a variavel `saudacao` armazena uma [lambda](/glossario/lambda/) diretamente. Nao existe nenhuma cerimonia extra -- basta declarar o tipo funcional e atribuir o bloco de código. Isso já e radicalmente diferente do que você encontra no Java, onde seria necessário usar interfaces funcionais ou classes anonimas.

## Funções de Alta Ordem

Uma [função de alta ordem](/glossario/higher-order-function/) (higher-order function) e aquela que recebe outras funções como parametro ou retorna uma função. A biblioteca padrão do Kotlin esta repleta delas: `map`, `filter`, `fold`, `reduce`, `flatMap`, entre muitas outras.

```kotlin
fun <T> List<T>.filtrarE(
    predicado1: (T) -> Boolean,
    predicado2: (T) -> Boolean
): List<T> {
    return this.filter { predicado1(it) && predicado2(it) }
}

fun main() {
    val numeros = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

    val resultado = numeros.filtrarE(
        predicado1 = { it > 3 },
        predicado2 = { it % 2 == 0 }
    )

    println(resultado) // [4, 6, 8, 10]
}
```

A função `filtrarE` recebe dois predicados e aplica ambos ao mesmo tempo. Esse padrão e muito útil para construir pipelines de filtragem flexiveis, especialmente quando os criterios vem de fontes diferentes, como inputs do usuário ou configurações dinamicas.

## Lambdas e Sintaxe Concisa

Kotlin oferece uma sintaxe de lambda extremamente enxuta. Quando uma lambda tem apenas um parametro, você pode usar `it` como referência implicita. Alem disso, se a lambda for o ultimo argumento de uma função, ela pode ficar fora dos parenteses.

```kotlin
val nomes = listOf("Ana", "Bruno", "Carlos", "Diana")

// Sintaxe completa
val maiusculas1 = nomes.map({ nome: String -> nome.uppercase() })

// Tipo inferido
val maiusculas2 = nomes.map({ nome -> nome.uppercase() })

// Lambda fora dos parenteses
val maiusculas3 = nomes.map { nome -> nome.uppercase() }

// Usando it
val maiusculas4 = nomes.map { it.uppercase() }

// Referência de funcao
val maiusculas5 = nomes.map(String::uppercase)
```

Todas as cinco formas produzem o mesmo resultado. No dia a dia, as formas mais concisas (com `it` ou referência de função) são as preferidas pela comunidade. Se você quer se aprofundar em como lambdas se conectam com coroutines, confira o [guia completo de coroutines](/guias/guia-coroutines-completo/).

## Funções Puras e Imutabilidade

Uma função pura e aquela que, dados os mesmos argumentos, sempre retorna o mesmo resultado e não causa efeitos colaterais. Esse conceito esta no coracao da programação funcional e Kotlin facilita muito a sua adoção por meio da palavra-chave `val` e das [coleções imutáveis](/glossario/collections/).

```kotlin
// Função pura: sem efeitos colaterais, resultado previsivel
fun calcularDesconto(preco: Double, percentual: Double): Double {
    return preco * (1.0 - percentual / 100.0)
}

// Uso de val para imutabilidade
data class Produto(val nome: String, val preco: Double)

fun aplicarDesconto(produtos: List<Produto>, percentual: Double): List<Produto> {
    return produtos.map { it.copy(preco = calcularDesconto(it.preco, percentual)) }
}

fun main() {
    val catálogo = listOf(
        Produto("Teclado", 250.0),
        Produto("Mouse", 120.0),
        Produto("Monitor", 1800.0)
    )

    val comDesconto = aplicarDesconto(catálogo, 10.0)
    println(comDesconto)
    // O catálogo original permanece intacto
    println(catálogo)
}
```

Repare que usamos `copy` da [data class](/glossario/data-class/) para criar novas instancias em vez de modificar as existentes. A lista original `catálogo` nunca e alterada. Esse padrão de [imutabilidade](/glossario/immutable/) reduz drasticamente os bugs relacionados a estado compartilhado, especialmente em ambientes concorrentes.

## Composição de Funções

Composição de funções e o ato de combinar funções simples para criar funções mais complexas. Em matematica, a composição de `f` e `g` e `f(g(x))`. Kotlin não tem um operador nativo de composição, mas e trivial criar um:

```kotlin
infix fun <A, B, C> ((B) -> C).comp(outra: (A) -> B): (A) -> C {
    return { a: A -> this(outra(a)) }
}

fun duplicar(x: Int): Int = x * 2
fun incrementar(x: Int): Int = x + 1
fun paraTexto(x: Int): String = "Resultado: $x"

fun main() {
    val pipeline = ::paraTexto comp ::duplicar comp ::incrementar

    println(pipeline(5))  // Resultado: 12
    // Execução: incrementar(5) = 6 -> duplicar(6) = 12 -> paraTexto(12)
}
```

A composição permite criar pipelines de transformacao reusaveis. Você define cada etapa isoladamente, testa cada uma separadamente e depois as compoe. Isso torna o código muito mais modular é fácil de manter.

## Currying

Currying e a técnica de transformar uma função que recebe múltiplos argumentos em uma sequência de funções que recebem um argumento de cada vez. Embora Kotlin não tenha currying nativo, implementa-lo e simples:

```kotlin
fun <A, B, C> curry(f: (A, B) -> C): (A) -> (B) -> C {
    return { a: A -> { b: B -> f(a, b) } }
}

fun somar(a: Int, b: Int): Int = a + b

fun main() {
    val somarCurried = curry(::somar)
    val somar5 = somarCurried(5)

    println(somar5(3))   // 8
    println(somar5(10))  // 15

    // Aplicação parcial diretamente
    val formatarMoeda = { simbolo: String ->
        { valor: Double ->
            "$simbolo %.2f".format(valor)
        }
    }

    val formatarReais = formatarMoeda("R$")
    println(formatarReais(1499.90))  // R$ 1499.90
}
```

Currying e particularmente útil quando você precisa de aplicação parcial -- fixar alguns parametros de uma função e gerar uma nova função mais específica. No exemplo acima, `somar5` e uma versão especializada de `somar` que sempre soma 5.

## Tail Recursion

Recursão e um pilar da programação funcional, mas recursão clássica pode causar `StackOverflowError` para entradas grandes. Kotlin resolve isso com a palavra-chave [tailrec](/glossario/tailrec/), que otimiza funções recursivas de cauda convertendo-as em loops internamente:

```kotlin
tailrec fun fatorial(n: Long, acumulador: Long = 1): Long {
    return if (n <= 1) acumulador
    else fatorial(n - 1, n * acumulador)
}

tailrec fun fibonacci(n: Int, a: Long = 0, b: Long = 1): Long {
    return if (n == 0) a
    else fibonacci(n - 1, b, a + b)
}

fun main() {
    println(fatorial(20))      // 2432902008176640000
    println(fibonacci(50))     // 12586269025
}
```

Para que `tailrec` funcione, a chamada recursiva precisa ser a ultima operação da função. O compilador verifica isso e emite um aviso se a anotacao for usada incorretamente. Essa otimização e a mesma que linguagens como Scala e Erlang fazem automaticamente.

## Avaliacao Lazy com Sequences

Quando você trabalha com [coleções](/glossario/collections/) grandes, as operações `map`, `filter` e similares criam coleções intermediarias a cada passo. Sequences resolvem esse problema com avaliacao lazy, processando os elementos um a um através de toda a cadeia de operações:

```kotlin
fun main() {
    val resultado = (1..1_000_000)
        .asSequence()
        .filter { it % 3 == 0 }
        .map { it * it }
        .filter { it > 1000 }
        .take(10)
        .toList()

    println(resultado)
    // [1089, 1296, 1521, 1764, 2025, 2304, 2601, 2916, 3249, 3600]
}
```

Sem `asSequence()`, cada operação geraria uma lista intermediaria de até um milhao de elementos. Com Sequences, os elementos são processados sob demanda e a cadeia para assim que os 10 elementos necessários são encontrados. Se você trabalha com fluxos assíncronos, o conceito de laziness também aparece no [Kotlin Flow](/glossario/flow/), que e a contraparte assíncrona das Sequences.

## A Biblioteca Arrow

Arrow e a biblioteca de programação funcional mais popular do ecossistema Kotlin. Ela traz estruturas de dados e padrões consagrados do mundo funcional, como `Either`, `Option`, `Validated` e suporte a efeitos computacionais.

```kotlin
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import arrow.core.raise.either

sealed class AppErro {
    data class UsuarioNaoEncontrado(val id: Long) : AppErro()
    data class SemPermissao(val acao: String) : AppErro()
}

fun buscarUsuario(id: Long): Either<AppErro, String> {
    return if (id > 0) "Usuario #$id".right()
    else AppErro.UsuarioNaoEncontrado(id).left()
}

fun verificarPermissao(usuario: String, acao: String): Either<AppErro, String> {
    return if (acao != "deletar") "$usuario pode $acao".right()
    else AppErro.SemPermissao(acao).left()
}

fun main() {
    val resultado = either {
        val usuario = buscarUsuario(42).bind()
        val permissao = verificarPermissao(usuario, "editar").bind()
        permissao
    }

    when (resultado) {
        is Either.Right -> println("Sucesso: ${resultado.value}")
        is Either.Left -> println("Erro: ${resultado.value}")
    }
    // Saida: Sucesso: Usuario #42 pode editar
}
```

O `Either` substitui o uso de exceptions para controle de fluxo, que e uma prática considerada inadequada na programação funcional. Com `Either.Left` representando o erro e `Either.Right` representando o sucesso, o fluxo de erro se torna explicito e composicional. O bloco `either` com `bind()` permite encadear operações que podem falhar de forma sequencial e limpa.

## Combinando Tudo na Prática

Vamos juntar vários conceitos em um exemplo mais proximo da realidade -- um pipeline de processamento de dados:

```kotlin
data class Pedido(
    val id: Long,
    val valor: Double,
    val status: String,
    val clienteId: Long
)

fun main() {
    val pedidos = listOf(
        Pedido(1, 150.0, "APROVADO", 100),
        Pedido(2, 85.0, "PENDENTE", 101),
        Pedido(3, 320.0, "APROVADO", 100),
        Pedido(4, 45.0, "CANCELADO", 102),
        Pedido(5, 210.0, "APROVADO", 101)
    )

    // Pipeline funcional: filtra, transforma e agrega
    val faturamentoPorCliente = pedidos
        .asSequence()
        .filter { it.status == "APROVADO" }
        .groupBy { it.clienteId }
        .mapValues { (_, pedidosCliente) ->
            pedidosCliente.sumOf { it.valor }
        }

    println(faturamentoPorCliente)
    // {100=470.0, 101=210.0}

    // Funções puras compostas
    val ehAprovado: (Pedido) -> Boolean = { it.status == "APROVADO" }
    val ehValorAlto: (Pedido) -> Boolean = { it.valor > 100.0 }

    val pedidosPremium = pedidos
        .filter { ehAprovado(it) && ehValorAlto(it) }
        .sortedByDescending { it.valor }

    println(pedidosPremium.map { it.id }) // [3, 5, 1]
}
```

Esse tipo de pipeline e extremamente comum em aplicações reais. Cada função faz uma coisa só, os dados fluem de uma transformacao para outra e o resultado final e previsivel e testavel. Para cenários mais complexos envolvendo processamento assíncrono, você pode migrar essas pipelines para [Flow](/guias/kotlin-flow-guia/) com poucas alteracoes.

## Conclusão

A programação funcional em Kotlin não e uma questao de tudo ou nada. O grande barato da linguagem e justamente permitir que você misture paradigmas conforme a necessidade. Use funções puras e imutabilidade como padrão, recorra a funções de alta ordem para abstrair comportamentos e lance mao de composição e currying quando a complexidade pedir. Com Arrow na jogada, você tem acesso a ferramentas sofisticadas sem precisar abandonar o ecossistema Kotlin que já conhece. Se você quer continuar se aprofundando, confira também o [guia de testes em Kotlin](/guias/guia-testes-kotlin/) para aprender a testar funções puras e pipelines funcionais de forma eficiente. Para quem se interessa por programação funcional em outras linguagens, <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust também oferece suporte robusto a iteradores, closures e imutabilidade por padrão</a>, e <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python possui ferramentas funcionais como map, filter e reduce integradas à linguagem</a>.
