This content originally appeared on DEV Community and was authored by Fernando Rosa
Olá, pessoal!
Há 8 meses, embarquei na jornada de ser um Lead Software Engineer no Nubank. Vindo de um mundo onde Kotlin e Go eram minhas principais ferramentas, mergulhar em Clojure foi uma mudança de paradigma. Hoje, quero compartilhar um pouco dessa experiência, mostrando com código as diferenças e o que torna Clojure uma linguagem tão fascinante de se trabalhar.
Vamos explorar três problemas simples, resolvidos em cada uma das três linguagens.
Problema 1: O Clássico "Olá, Mundo!"
Tudo começa aqui. Ver a sintaxe mais básica já nos dá uma pista da filosofia de cada linguagem.
Go:
Focado em simplicidade e um ferramental robusto. Tudo é explícito.
package main
import "fmt"
func main() {
fmt.Println("Olá, Mundo!")
}
Kotlin:
Moderno, conciso e interoperável com Java. A sintaxe é familiar para quem vem do mundo OO.
fun main() {
println("Olá, Mundo!")
}
Clojure:
Aqui a primeira "estranheza" que vira um encanto. A sintaxe LISP, com parênteses e prefixos (função argumento), trata código como dados. É simples e incrivelmente poderosa.
(println "Olá, Mundo!")
Análise Rápida: De cara, a concisão de Clojure se destaca. A ausência de cerimônias como declaração de package ou main para um script simples já mostra o foco em ir direto ao ponto.
Problema 2: Transformação de Dados – Agrupar e Somar Vendas
Este é um cenário do dia a dia: temos uma lista de vendas e queremos calcular o total por produto. É aqui que a abordagem funcional de Clojure realmente brilha.
Digamos que temos estes dados:
[{"produto": "A", "valor": 10}, {"produto": "B", "valor": 20}, {"produto": "A", "valor": 5}]
Go:
Em Go, faríamos isso de forma imperativa, inicializando um mapa e iterando sobre a lista para acumular os valores. É eficiente, mas verboso.
package main
import "fmt"
type Venda struct {
Produto string
Valor int
}
func main() {
vendas := []Venda{
{"A", 10},
{"B", 20},
{"A", 5},
}
totalPorProduto := make(map[string]int)
for _, v := range vendas {
totalPorProduto[v.Produto] += v.Valor
}
fmt.Println(totalPorProduto)
// Output: map[A:15 B:20]
}
Kotlin:
Kotlin oferece uma API de coleções rica e funcional, tornando o código mais expressivo e menos propenso a erros.
data class Venda(val produto: String, val valor: Int)
fun main() {
val vendas = listOf(
Venda("A", 10),
Venda("B", 20),
Venda("A", 5)
)
val totalPorProduto = vendas
.groupBy { it.produto }
.mapValues { entry ->
entry.value.sumOf { it.valor }
}
println(totalPorProduto)
// Output: {A=15, B=20}
}
Clojure:
Em Clojure, a transformação de dados é o coração da linguagem. O código é uma composição de funções, resultando em uma "pipeline" de dados clara e elegante.
(def vendas
[{:produto "A" :valor 10}
{:produto "B" :valor 20}
{:produto "A" :valor 5}])
(def total-por-produto
(->> vendas
(group-by :produto)
(map (fn [[produto lista-vendas]]
[produto (reduce + (map :valor lista-vendas))]))
(into {})))
(println total-por-produto)
; Output: {"A" 15, "B" 20}
Análise Rápida: Enquanto Go é explícito e manual, Kotlin e Clojure mostram o poder das abstrações funcionais. A solução em Clojure, com o macro ->> (thread-last), descreve perfeitamente o fluxo: pegue as vendas, agrupe por :produto, depois mapeie cada grupo para calcular a soma e, por fim, transforme tudo em um mapa. É como ler uma receita.
Problema 3: Concorrência – Incrementando um Contador com Segurança
Como lidar com estado compartilhado é um desafio central em sistemas concorrentes. Cada linguagem tem sua abordagem. Vamos simular 1.000 "processos" incrementando um contador.
Go:
Goroutines e Channels são os cidadãos de primeira classe para concorrência em Go. Para estado mutável compartilhado, usamos mutex para garantir a segurança.
package main
import (
"fmt"
"sync"
)
func main() {
var contador int
var wg sync.WaitGroup
var mu sync.Mutex
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
contador++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("Contador final:", contador)
// Output: Contador final: 1000
}
Kotlin:
Coroutines são a resposta de Kotlin para concorrência leve. Para estado compartilhado, podemos usar tipos atômicos do Java ou um Mutex específico para coroutines.
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
val mutex = Mutex()
var contador = 0
fun main() = runBlocking {
val jobs = List(1000) {
launch(Dispatchers.Default) {
mutex.withLock {
contador++
}
}
}
jobs.forEach { it.join() }
println("Contador final: $contador")
// Output: Contador final: 1000
}
Clojure:
Clojure abraça a imutabilidade e fornece construções simples e poderosas para gerenciar estado quando ele é inevitável. O atom é perfeito para estado compartilhado e não coordenado. A função swap! garante atualizações atômicas.
(def contador (atom 0))
(defn incrementar []
(swap! contador inc))
(let [processos (repeatedly 1000 #(future (incrementar)))]
(doseq [p processos] (deref p))) ; Espera todos terminarem
(println "Contador final:" @contador)
; Output: Contador final: 1000
Análise Rápida: As três linguagens resolvem o problema com segurança, mas a abordagem de Clojure é notavelmente mais limpa. Não há locks manuais visíveis no nosso código de negócio. A complexidade da concorrência é abstraída pelo atom e pela função swap!, tornando o código mais simples de ler e escrever.
Conclusão
Trabalhar com Go e Kotlin me deu uma base sólida em sistemas eficientes e bem tipados. Mas a imersão em Clojure no Nubank me ensinou a amar a simplicidade, a imutabilidade e o poder da programação funcional.
A capacidade de moldar o código como uma sequência de transformações de dados e de lidar com concorrência de forma tão elegante não só torna o desenvolvimento mais rápido, mas também mais prazeroso. É uma linguagem que nos convida a pensar no problema de forma diferente e, na minha opinião, de um jeito muito mais direto e poderoso.
E você, já teve uma experiência parecida ao aprender uma nova linguagem que mudou sua forma de pensar? Adoraria saber!
This content originally appeared on DEV Community and was authored by Fernando Rosa
Fernando Rosa | Sciencx (2025-08-12T00:29:48+00:00) De Kotlin e Go para Clojure: uma jornada de 8 meses no Nubank.. Retrieved from https://www.scien.cx/2025/08/12/de-kotlin-e-go-para-clojure-uma-jornada-de-8-meses-no-nubank/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.