| Bellacosa Mainframe com monk investigando o clojure |
☕ Um Café no Bellacosa Mainframe
Monk, COBOL e o Estranho Caso dos 847 Parênteses: Anatomia Técnica de Clojure para Quem Ainda Confia numa Boa DATA DIVISION
🔎 JVM, bytecode, Reader, Compiler, REPL, namespaces, deps.edn, AOT, JAR, EDN, macros, Java interop, imutabilidade, testes e a investigação obsessivamente organizada de uma linguagem em que cada parêntese precisa estar exatamente onde deveria estar
Adrian Monk entrou no CPD às 08h13.
Às 08h14 percebeu que três cabos Ethernet estavam cruzados.
Às 08h15 alinhou os manuais do Db2 pela altura.
Às 08h17 encontrou o verdadeiro problema.
Na tela havia isto:
(defn calcula-saldo [saldo debito]
(- saldo debito))
Monk ficou imóvel.
— Natalie...
— Sim, Mr. Monk?
— Há quatro parênteses.
— É Clojure.
— Eu sei.
— Então qual é o problema?
Monk aproximou-se da tela.
— Eles precisam fechar na ordem correta.
O programador COBOL sentado ao lado sorriu.
Finalmente havia encontrado alguém que entendia sua obsessão por organização.
Pegue o café.
Na viagem anterior conhecemos a filosofia de Clojure. Agora vamos abrir a máquina, retirar a tampa e descobrir como ela realmente funciona.
1. Antes de tudo: Clojure não é “um script Lisp interpretado”
Essa é provavelmente a primeira ficha que precisamos organizar na mesa de evidências.
A implementação principal de Clojure executa sobre a Java Virtual Machine.
E há uma informação fundamental na documentação oficial:
Clojure não possui interpretador no sentido comum.
O código carregado é compilado para bytecode JVM. Normalmente essa compilação acontece dinamicamente durante o carregamento; Clojure também suporta compilação Ahead-of-Time, ou AOT. (Clojure)
A cadeia mental correta é:
SOURCE CODE
.clj
│
▼
CLOJURE READER
│
▼
CLOJURE DATA STRUCTURES
│
▼
MACRO EXPANSION
│
▼
CLOJURE COMPILER
│
▼
JVM BYTECODE
│
▼
JVM / JIT
│
▼
MACHINE CODE
Isso já destrói aquela impressão:
Clojure
=
linguagemzinha interpretada
Não.
Estamos em território JVM.
A versão estável atual, Clojure 1.12.5, lançada em 12 de maio de 2026, continua gerando bytecode compatível com Java 8, embora possa executar em JVMs modernas. (Clojure)
2. Monk encontra o Reader
Aqui Clojure começa a ficar realmente interessante.
Em muitas linguagens imaginamos:
arquivo fonte
↓
parser
↓
AST
↓
compiler
Clojure possui algo chamado:
Reader
Ele transforma caracteres em estruturas de dados Clojure. E isso está ligado a uma propriedade clássica dos Lisps chamada homoiconicidade: programas são representados usando estruturas da própria linguagem. (Clojure)
Veja:
(+ 10 20)
O Reader não enxerga simplesmente uma string contendo código.
Ele reconhece uma lista:
(
símbolo +
número 10
número 20
)
Conceitualmente:
SOURCE
(+ 10 20)
↓ Reader
LIST
├── Symbol: +
├── Number: 10
└── Number: 20
Isso é importantíssimo.
Porque ajuda a explicar por que macros Lisp são tão poderosas.
Código já nasce próximo de dados manipuláveis pela própria linguagem.
Monk observa.
— Então primeiro precisamos saber exatamente o que estamos olhando.
— Sim.
— Gostei dessa linguagem.
3. Form não é statement
Outro choque para o COBOLzeiro.
COBOL possui statements:
MOVE
ADD
SUBTRACT
PERFORM
IF
READ
WRITE
Clojure é estruturada fundamentalmente em expressões/forms que produzem valores. A referência oficial ressalta que não existe a mesma distinção tradicional entre declarations e statements; forms são avaliadas e produzem resultados, embora alguns resultados possam ser ignorados quando buscamos apenas efeitos colaterais. (Clojure)
Exemplo:
(+ 10 20)
produz:
30
Até:
(if (> saldo 100)
"APROVADO"
"NEGADO")
produz um valor.
Compare com a mentalidade COBOL:
IF WS-SALDO > 100
MOVE 'APROVADO' TO WS-STATUS
ELSE
MOVE 'NEGADO' TO WS-STATUS
END-IF.
Clojure:
(def status
(if (> saldo 100)
"APROVADO"
"NEGADO"))
A expressão if retorna aquilo que será associado a status.
Essa pequena diferença influencia enormemente o estilo da linguagem.
4. Nosso primeiro programa: Hello World
Vamos fazer o equivalente ao ritual iniciático de toda linguagem.
COBOL:
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
PROCEDURE DIVISION.
DISPLAY 'HELLO WORLD'.
STOP RUN.
Clojure:
(println "Hello World")
Resultado:
Hello World
Monk olha para os dois.
Olha novamente.
— Está faltando alguma coisa.
— Não.
— DATA DIVISION?
— Não precisa.
— PROCEDURE DIVISION?
— Não existe.
— STOP RUN?
— Não.
Monk começa a suar.
😂
5. Agora faça direito: namespace
Aplicação real não deveria ser apenas um arquivo jogado na mesa.
Criamos algo como:
(ns bellacosa.core)
Depois:
(ns bellacosa.core)
(defn -main []
(println "Hello Bellacosa Mainframe"))
O ns define o namespace.
Namespaces ajudam a organizar:
symbols
functions
Vars
dependencies
aliases
A convenção oficial liga o namespace ao caminho do arquivo. Pontos no nome correspondem a diretórios, e hífens são convertidos em underscores no nome físico do recurso. (Clojure)
Assim:
bellacosa.core
normalmente corresponde a:
src/
└── bellacosa/
└── core.clj
Isso é convenção importante.
Não invente:
src/
coisas/
arquivo-final-agora-vai-3.clj
Monk descobrirá.
6. deps.edn: o pequeno arquivo que organiza a expedição
No ecossistema atual encontramos frequentemente:
deps.edn
EDN significa Extensible Data Notation.
Um projeto mínimo pode ter:
bellacosa/
│
├── deps.edn
│
└── src/
└── bellacosa/
└── core.clj
Um deps.edn simples:
{:paths ["src"]
:deps {}}
Com dependência:
{:paths ["src"]
:deps
{org.clojure/clojure
{:mvn/version "1.12.5"}}}
deps.edn descreve elementos necessários para formar o classpath, incluindo paths, dependências, repositórios e aliases usados pelas ferramentas Clojure CLI/tools.deps. (Clojure)
Para um mainframeiro, pense vagamente:
JCL / PROC / STEPLIB / SYSLIB
versus:
deps.edn
classpath
dependencies
aliases
Não são equivalentes.
Mas ambos respondem, em universos diferentes, à pergunta:
“Onde estão as coisas necessárias para executar isso?”
7. Clojure CLI
Com as ferramentas instaladas você encontrará comandos como:
clj
ou:
clojure
Um deles pode iniciar o REPL.
Você digita:
user=> (+ 10 20)
30
Depois:
user=> (def saldo 1000M)
#'user/saldo
Depois:
user=> (- saldo 200M)
800M
E isso nos leva a uma diferença monumental em relação ao COBOL tradicional.
8. O REPL não é brinquedo
REPL:
READ
EVAL
PRINT
LOOP
O programador COBOL tradicional pensa:
EDIT
↓
COMPILE
↓
LINK
↓
EXECUTE
↓
ABEND
↓
JES
↓
SYSOUT
O desenvolvedor Clojure frequentemente trabalha:
EDITOR
↕
REPL
↕
PROCESSO VIVO
Ele escreve uma função.
Envia a função ao REPL.
Executa.
Inspeciona dados.
Altera.
Reavalia.
Testa novamente.
Essa interatividade é parte central da cultura Clojure.
9. Editor: onde escrevemos isso?
Clojure não exige uma IDE oficial única.
É comum encontrar:
VS Code + Calva
Emacs + CIDER
IntelliJ IDEA + Cursive
Neovim + LSP
Além disso:
clojure-lsp
nREPL
são peças comuns do ecossistema.
O importante para o iniciante é entender uma coisa:
Escolha um editor com integração REPL decente.
Usar Clojure como se fosse C:
editar arquivo
salvar
executar programa inteiro
olhar resultado
funciona.
Mas você estará ignorando uma das melhores características do ambiente.
10. Compilação dinâmica
Quando Clojure carrega código, ele é compilado.
A documentação é explícita:
não existe a necessidade de um estágio de compilação separado para que uma função deixe de ser “interpretada”; Clojure não possui interpretador tradicional. (Clojure)
Então:
(defn soma [a b]
(+ a b))
não significa:
guardar texto
↓
interpretar linha
↓
interpretar próxima linha
Existe compilação para JVM bytecode.
A JVM, por sua vez, pode aplicar JIT.
Portanto:
CLOJURE SOURCE
↓
CLOJURE COMPILER
↓
BYTECODE
↓
JVM
↓
JIT
↓
NATIVE EXECUTION
É uma diferença técnica importantíssima.
11. AOT — Ahead-of-Time Compilation
Mas também podemos compilar antecipadamente.
Por quê?
A documentação oficial cita razões como:
reduzir trabalho durante startup;
entregar aplicação sem fontes;
gerar classes nomeadas para consumo Java;
trabalhar em ambientes onde geração dinâmica de bytecode/classloaders seja indesejada. (Clojure)
A unidade de compilação AOT é um namespace.
A compilação pode produzir vários .class.
A documentação explica inclusive que cada arquivo, função e gen-class pode resultar em classes e que existe uma classe loader __init. (Clojure)
Ou seja:
bellacosa/core.clj
↓
AOT
↓
bellacosa/core*.class
Agora estamos completamente no território JVM.
12. Então Clojure vira Java?
Não.
Isso é importante.
O fato de gerar JVM bytecode não transforma semanticamente Clojure em Java.
Seria como dizer:
COBOL compilado virou Assembler.
Não.
O target de compilação não define sozinho a linguagem-fonte.
Clojure preserva conceitos como:
persistent data structures
Vars
keywords
symbols
macros
protocols
multimethods
sequences
dynamic typing
mas aproveita a JVM como runtime.
13. JAR: empacotando a criatura
Aplicações JVM frequentemente são distribuídas em:
.jar
Um JAR é essencialmente um arquivo ZIP estruturado contendo:
.class
resources
metadata
manifest
Dependendo da ferramenta de build, você pode produzir um JAR contendo a aplicação e, em algumas estratégias, dependências necessárias.
Então podemos chegar a algo conceitualmente como:
SOURCE
↓
BUILD
↓
JAR
↓
java -jar app.jar
Isso facilita executar Clojure dentro de infraestrutura Java existente.
14. Java interop: aqui o caso fica interessante
Clojure pode chamar Java diretamente.
Exemplo:
(System/currentTimeMillis)
Ou:
(.toUpperCase "cobol")
Resultado:
"COBOL"
Criando objeto:
(java.util.Date.)
Importando:
(import java.time.LocalDate)
Depois:
(LocalDate/now)
Portanto o universo disponível não é apenas:
bibliotecas Clojure
É potencialmente:
Clojure ecosystem
+
Java ecosystem
Isso muda completamente a análise empresarial.
15. Tipagem dinâmica não significa ausência de tipos
Este erro precisa desaparecer.
Clojure é dinamicamente tipada.
Mas os valores possuem tipos.
(type 42)
não significa que 42 seja uma nuvem metafísica sem representação.
A diferença principal é que você normalmente não declara:
int idade = 42;
como faria em Java.
Você escreve:
(def idade 42)
O tipo está associado ao valor em runtime.
Isso traz flexibilidade.
Também transfere certas classes de erro que linguagens estaticamente tipadas podem detectar durante compilação para testes, runtime, contracts/specs e outras técnicas.
16. nil: o suspeito que sempre volta
Clojure possui:
nil
E apenas:
nil
false
são falsy.
Isto é verdadeiro:
(if 0
"verdade"
"falso")
Resultado:
"verdade"
Assim como uma coleção vazia é truthy.
Monk anota imediatamente:
NÃO ASSUMIR
QUE 0 = FALSE.
Boa prática.
17. Keywords: pequenas coisas extremamente importantes
Veja:
:nome
Isso é uma keyword.
Maps usam muito:
{:nome "Adrian Monk"
:profissao "detetive"
:saldo 1000M}
Podemos acessar:
(:nome cliente)
Keywords funcionam como funções sobre mapas em muitos casos.
Mais importante em aplicações grandes é usar keywords qualificadas:
:cliente/id
:cliente/nome
:conta/saldo
Isso reduz colisões semânticas.
E se você estudou Datomic, essas formas devem soar muito familiares.
Não é coincidência.
18. Maps são fundamentais
Em Java tradicional você talvez criasse:
class Cliente {
String nome;
BigDecimal saldo;
}
Clojure frequentemente trabalha simplesmente com:
{:cliente/nome "Natalie"
:cliente/saldo 1000M}
Dados permanecem dados.
Não precisamos criar uma classe nova para cada estrutura intermediária.
Isso reduz cerimônia.
Mas também exige disciplina.
Se todo desenvolvedor inventar:
:nome
:Nome
:customer-name
:clientName
:nm_cliente
Monk terá um colapso nervoso.
19. Namespace bem organizado
Um arquivo real:
(ns bellacosa.conta
(:require
[clojure.string :as str]))
Depois:
(defn normalizar-nome [nome]
(str/upper-case nome))
Chamamos:
(normalizar-nome "adrian monk")
Resultado:
"ADRIAN MONK"
Observe:
[clojure.string :as str]
Criamos alias.
Isso permite:
str/upper-case
em vez de repetir:
clojure.string/upper-case
Boa organização de namespaces é uma das primeiras disciplinas que um desenvolvedor Clojure profissional precisa dominar.
20. Convenções importam
Clojure possui convenções próprias.
Funções:
calcular-saldo
em vez de:
calcularSaldo
Predicados frequentemente terminam com ?:
positivo?
valido?
autorizado?
Funções que expressam mutação/efeito significativo frequentemente podem usar ! como convenção:
swap!
reset!
send!
Isso não é decoração.
Ajuda a leitura.
Compare:
(autorizado? cliente)
Você praticamente lê:
autorizado cliente?
Muito Lisp.
21. Não programe Java usando parênteses
Este talvez seja o maior erro de iniciante.
Ele aprende Clojure e tenta recriar:
ClienteClass
ClienteService
ClienteRepository
ClienteFactory
ClienteManager
com Clojure.
Resultado:
Java
vestido de Lisp.
Não faça isso.
Comece perguntando:
Quais são meus dados?
Quais transformações aplico?
Onde realmente existe estado?
Onde existem efeitos colaterais?
Esse é o salto mental.
22. Functional core, imperative shell
Uma excelente disciplina arquitetural é manter grande parte da regra de negócio como funções puras.
Imagine:
(defn aplicar-debito [conta valor]
(update conta :saldo - valor))
Entrada:
{:saldo 1000M}
Saída:
{:saldo 800M}
Sem:
database
HTTP
Kafka
filesystem
global state
Depois outra camada faz:
LER BANCO
↓
FUNÇÃO PURA
↓
GRAVAR BANCO
Isso facilita tremendamente testes.
23. Testando
Imagine:
(defn somar [a b]
(+ a b))
Com clojure.test:
(ns bellacosa.core-test
(:require
[clojure.test :refer [deftest is testing]]
[bellacosa.core :as sut]))
(deftest teste-soma
(testing "soma dois valores"
(is (= 30
(sut/somar 10 20)))))
Observe:
=
verifica igualdade.
E:
is
expressa expectativa.
Funções puras são deliciosamente testáveis:
INPUT
↓
FUNCTION
↓
EXPECTED OUTPUT
Sem preparar vinte mocks.
24. spec: descrevendo contratos de dados
O Clojure atual inclui como dependências centrais spec.alpha e core.specs.alpha. (Clojure)
A ideia de Spec é permitir descrever propriedades/estruturas dos dados.
Conceitualmente:
(s/def ::saldo decimal?)
Você pode especificar estruturas e usar specs para validação, testes generativos e instrumentação.
Isso é particularmente importante numa linguagem dinâmica.
Mas não cometa o erro oposto:
TIPAGEM DINÂMICA
↓
PÂNICO
↓
SPEC EM CADA ÁTOMO DO UNIVERSO
Use contratos onde os limites e invariantes realmente importam.
25. Macros: Monk encontra uma passagem secreta
Considere:
(when autorizado?
(processar))
when é uma macro.
Macros recebem formas de código e produzem outras formas.
Isso é possível de maneira particularmente natural porque código Lisp é representado por estruturas da própria linguagem.
Conceitualmente:
SOURCE CODE
↓
DATA STRUCTURE
↓
MACRO
↓
NEW CODE STRUCTURE
↓
COMPILER
É extremamente poderoso.
E perigosíssimo quando usado para mostrar inteligência.
Boa regra:
Se uma função resolve, provavelmente use uma função.
Macros são para quando você realmente precisa transformar a própria estrutura do código.
26. Performance: o caso não termina na JVM
Clojure pode ser rápido.
Mas existem suspeitos conhecidos:
boxing
reflection
lazy sequences
allocation
GC
dynamic dispatch
Em Java interop, por exemplo, type hints podem evitar reflection:
(defn tamanho [^String texto]
(.length texto))
Mas novamente:
PROFILING FIRST.
Monk concorda.
Você não prende o suspeito antes de encontrar evidências.
27. Lazy sequences: maravilhosas até incendiarem a memória
Clojure utiliza lazy sequences extensivamente.
Exemplo:
(take 10 (range))
range pode representar uma sequência conceitualmente enorme.
Só precisamos dos primeiros dez.
Fantástico.
Mas laziness mal utilizada pode reter referências e aumentar consumo de memória.
Portanto:
LAZY
≠
FREE
O programador precisa entender:
realization
chunking
retained references
allocation
Especialmente em pipelines grandes.
28. Transducers
Quando você evolui, encontrará transducers.
Imagine:
(comp
(filter even?)
(map #(* % 10)))
Isso descreve uma transformação independentemente do mecanismo específico que consumirá os dados.
É uma ideia poderosa para composição eficiente de pipelines.
Mas não comece seu primeiro dia tentando dominar transducers.
Monk talvez consiga.
Você não precisa.
29. Protocols e multimethods
Clojure não depende de classes OO para polimorfismo.
Temos recursos como:
protocols
multimethods
Protocols permitem definir conjuntos de operações implementáveis por diferentes tipos.
Multimethods permitem dispatch baseado em funções arbitrárias, indo além do tradicional:
dispatch pelo tipo da classe
Isso oferece polimorfismo sem obrigar a modelagem OO clássica.
30. defrecord
Quando maps simples deixam de ser suficientes para determinadas necessidades de representação/interoperabilidade, existe:
(defrecord Cliente
[id nome saldo])
Mas novamente:
não transforme todo map em record só porque Java ensinou que toda coisa merece uma classe.
Clojure tende a valorizar estruturas genéricas simples.
31. Tratamento de exceções
Sim, temos:
try
catch
finally
throw
Exemplo:
(try
(/ 10 0)
(catch ArithmeticException e
(println "Monk encontrou o erro.")))
Como estamos na JVM, exceções Java fazem parte do mundo.
Mas regra de negócio normal não deveria virar um festival de exceptions usadas como controle de fluxo.
32. Logging, observabilidade e produção
Uma aplicação Clojure bancária não vive apenas de:
(println "deu ruim")
Você precisa pensar em:
structured logging
metrics
distributed tracing
correlation IDs
latency
JVM metrics
heap
GC
thread pools
HTTP metrics
database metrics
Kafka lag
A linguagem pode ser funcional.
Produção continua sendo produção.
Monk pergunta:
— Quem alterou esse saldo?
Se seus logs responderem:
não sabemos
o paradigma funcional não salvará ninguém.
33. O que um COBOLzeiro já sabe e talvez não perceba
Existe muita bagagem transferível.
COBOL
DATA DIVISION
Você entende importância da estrutura dos dados.
Clojure
maps
vectors
sets
EDN
Dados continuam centrais.
COBOL
Você entende:
batch
transação
integridade
restart
Clojure
Precisará aplicar isso em:
services
events
Kafka
APIs
Datomic
COBOL
Você conhece:
COPYBOOK
Clojure
Você precisará entender:
namespaces
libraries
contracts
data schemas
Não são equivalentes.
Mas a disciplina mental viaja bem.
34. Normas e padrões: existe um ANSI Clojure?
Aqui encontramos uma diferença enorme.
COBOL possui longa história de padronização formal, incluindo padrões ANSI/ISO.
Clojure não é definida por um grande padrão ISO equivalente.
A referência prática da linguagem é formada pela implementação oficial, documentação, reference pages, API e processo de evolução mantido pelo core team.
O desenvolvimento é deliberadamente conservador, com forte preocupação com compatibilidade retroativa. Atualmente o core é desenvolvido por uma equipe no Nubank, que apoia esse trabalho. (Clojure)
Isso significa:
COBOL
ISO / ANSI
↓
vendors
↓
implementations
versus, simplificando:
CLOJURE
canonical implementation
+
official reference
+
core team
É outro modelo de governança.
35. Compatibilidade é uma obsessão curiosamente mainframe
Aqui Clojure surpreende.
A equipe oficial enfatiza evolução cuidadosa e backward compatibility. (Clojure)
Para uma linguagem associada a startups e fintechs, isso soa quase... mainframe.
O COBOLzeiro pergunta:
— Então eles evitam quebrar código antigo?
— Sim.
— Mudam a linguagem com cautela?
— Sim.
— Valorizam estabilidade?
— Sim.
— Tem certeza de que isso não foi criado em Poughkeepsie?
😂
36. GitHub e contribuição: outra peculiaridade
O código oficial está no repositório Clojure no GitHub e é distribuído sob Eclipse Public License 1.0. (GitHub)
Mas o desenvolvimento do core não funciona simplesmente como:
fork
↓
PR aleatório
↓
merge
Existe um processo próprio de contribuição, discussão e avaliação.
E há uma curiosidade muito atual: as regras oficiais de contribuição dizem explicitamente que código gerado por LLMs não é aceito como contribuição para o core por incompatibilidade com os compromissos do Contributor Agreement, salvo situações específicas envolvendo geradores escritos por humanos. (Clojure)
Isso é um belo Easter egg de 2026:
Você pode pedir a uma IA que explique Clojure.
Mas não pode simplesmente mandar o código dela para dentro do Clojure core.
Monk aprova.
— Precisamos saber exatamente quem escreveu cada linha.
37. O checklist Monk para um novo desenvolvedor Clojure
Antes de alguém se declarar desenvolvedor Clojure, eu gostaria que soubesse pelo menos:
Reader, forms e evaluation; namespaces e organização física dos arquivos; collections, sequences, maps e keywords;
def,defn,let,if,cond,case,map,filterereduce; funções de ordem superior, imutabilidade e persistent data structures; REPL-driven development; atoms, refs e noções de concorrência; Java interop e JVM;deps.edn, classpath e dependências; testes comclojure.test; tratamento de exceções; lazy sequences; macros sem abusar delas; profiling, heap e GC; e, acima de tudo, a diferença entre valor, identidade, estado e tempo.
Essa última linha é a que prepara o caminho para Datomic.
38. A comparação técnica final
| Característica | COBOL | Clojure |
|---|---|---|
| Origem | 1959 | 2007 |
| Paradigma dominante | Imperativo/procedural | Funcional |
| Tipagem | Estática | Dinâmica |
| Estrutura | Divisions/Sections/Paragraphs | Forms/namespaces/functions |
| Dados | Records/copybooks | Maps/vectors/sets |
| Mutabilidade | Normal | Desencorajada |
| Compilação | Compilador nativo/mainframe | JVM bytecode |
| Runtime | LE/z/OS etc. | JVM |
| Interatividade | Tradicionalmente baixa | REPL central |
| Concorrência | Plataforma/runtime | Recursos JVM + Clojure |
| Metaprogramação | Limitada | Macros Lisp |
| Dependências | Mainframe tooling | deps.edn/Maven ecosystem |
| Padronização | ANSI/ISO | Implementação/referência oficial |
| Ecossistema | Mainframe empresarial | JVM/funcional |
| Interop | CICS, Db2, IMS, LE etc. | Java/JVM |
| Filosofia | Procedimentos sobre registros | Transformações sobre valores |
39. Monk encontra o verdadeiro defeito
São 18h42.
O programa está funcionando.
Testes verdes.
REPL funcionando.
Heap estável.
GC normal.
Namespaces organizados.
Monk continua olhando para o código.
Natalie pergunta:
— O que foi agora?
Ele aponta:
(defn ProcessarCliente [Cliente]
...)
Silêncio.
— Mr. Monk?
— Maiúsculas.
— Funciona.
— Eu sei.
— Então deixe.
— Não posso.
Ele corrige:
(defn processar-cliente [cliente]
...)
Respira profundamente.
— Agora podemos colocar em produção.
☕ Epílogo — O COBOLzeiro fecha o caso
Existe uma tentação perigosa quando encontramos Clojure pela primeira vez.
Olhar:
(->> clientes
(filter ativo?)
(map calcular-risco)
(sort-by :score))
e concluir:
“Isso é simples demais para aplicações bancárias sérias.”
Mas simplicidade sintática não significa simplicidade computacional.
Por baixo daquele pequeno programa podem existir:
Clojure Reader
↓
macro expansion
↓
compiler
↓
JVM bytecode
↓
classloader
↓
HotSpot JIT
↓
garbage collector
↓
threads
↓
network
↓
Kafka
↓
Datomic
↓
AWS
É uma pilha tecnológica sofisticada.
O COBOL também sofre do preconceito inverso.
Alguém vê:
MOVE A TO B
e pensa:
“Linguagem primitiva.”
Até descobrir que aquele MOVE está dentro de um programa CICS conectado a Db2, MQ, RACF, Parallel Sysplex, WLM, SMF e bilhões de dólares de operações.
Nunca julgue uma arquitetura pelo tamanho do verbo.
Clojure ensina algo especialmente interessante ao programador COBOL.
Apesar de separados por quase cinquenta anos, ambos valorizam dados de maneira muito explícita.
COBOL colocou isso literalmente numa:
DATA DIVISION
Clojure coloca dados no centro usando:
{:cliente/id 123
:cliente/nome "Adrian Monk"
:cliente/saldo 1000M}
A diferença fundamental aparece quando chega a mudança.
COBOL pergunta:
COMO ALTERO O ESTADO?
Clojure frequentemente pergunta antes:
PRECISO REALMENTE ALTERAR ESTE ESTADO?
Essa pergunta conduz à imutabilidade.
A imutabilidade conduz a uma abordagem diferente de concorrência.
Essa abordagem conduz naturalmente às ideias de valores ao longo do tempo.
E quando seguimos esse túnel suficientemente longe...
encontramos novamente:
DATOMIC
É por isso que estudar Clojure antes de tentar compreender profundamente Datomic é tão revelador.
Você finalmente percebe que Datomic não apareceu de um disco voador.
Existe uma linha intelectual:
LISP
↓
CLOJURE
↓
VALUES
↓
IMMUTABILITY
↓
IDENTITY ≠ VALUE
↓
TIME
↓
DATOMIC
E isso explica também parte da história tecnológica do Nubank.
Não escolheram simplesmente uma linguagem cheia de parênteses e um banco estranho.
Adotaram uma família coerente de ideias sobre como controlar complexidade, estado, concorrência e tempo.
Se essas escolhas servem para qualquer empresa é outra investigação.
E Monk jamais encerraria um caso sem fazer essa pergunta.
Antes de aprovar Clojure para seu próximo sistema, ele alinharia cuidadosamente três papéis sobre a mesa:
PROBLEMA
ARQUITETURA
TECNOLOGIA
Depois verificaria se estão nessa ordem.
Porque se você começar pela tecnologia e sair procurando um problema para justificá-la...
há algo errado.
Não sabemos ainda o quê.
Mas Monk sabe.
É uma bênção... e uma maldição.
☕ Fim do caso.
Para continuar tecnicamente, as melhores portas são a Referência oficial do Clojure, o guia oficial de aprendizado e a documentação específica sobre compilação e AOT.
Sem comentários:
Enviar um comentário