☕ Um Café no Bellacosa Mainframe
Gostou do conteúdo? Ajude a manter o café quente, o COBOL compilando e o mainframe acordado. 😄
☕ Pague um café ao Bellacosa

Translate

sexta-feira, 18 de outubro de 2024

Monk, COBOL e o Estranho Caso dos 847 Parênteses: Anatomia Técnica de Clojure para Quem Ainda Confia numa Boa DATA DIVISION

 

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, filter e reduce; 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 com clojure.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ísticaCOBOLClojure
Origem19592007
Paradigma dominanteImperativo/proceduralFuncional
TipagemEstáticaDinâmica
EstruturaDivisions/Sections/ParagraphsForms/namespaces/functions
DadosRecords/copybooksMaps/vectors/sets
MutabilidadeNormalDesencorajada
CompilaçãoCompilador nativo/mainframeJVM bytecode
RuntimeLE/z/OS etc.JVM
InteratividadeTradicionalmente baixaREPL central
ConcorrênciaPlataforma/runtimeRecursos JVM + Clojure
MetaprogramaçãoLimitadaMacros Lisp
DependênciasMainframe toolingdeps.edn/Maven ecosystem
PadronizaçãoANSI/ISOImplementação/referência oficial
EcossistemaMainframe empresarialJVM/funcional
InteropCICS, Db2, IMS, LE etc.Java/JVM
FilosofiaProcedimentos sobre registrosTransformaçõ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

De Fã para Fã. Conteúdo não oficial produzido como homenagem, comentário, análise ou paródia. Personagens, marcas e obras eventualmente mencionados pertencem aos seus respectivos titulares.
Vagner Renato Bellacosa, IBM Champion e especialista em IBM Mainframe
IBM Z17 SYSTEM ONLINE
Sobre o autor

Vagner Renato Bellacosa

IBM Champion 2026 • Especialista em IBM Mainframe

Vagner Renato Bellacosa trabalha com IBM Mainframe desde 1988 , é IBM Champion e especialista em COBOL, CICS, Db2, z/OS e IBM Z. Compartilha experiências profissionais, conhecimento técnico, história da computação e práticas do universo mainframe para aproximar novas gerações das tecnologias que sustentam empresas, bancos e governos ao redor do mundo.

IBM Champion IBM Z COBOL CICS Db2 z/OS Mainframe desde 1988
GitHub LinkedIn
Inicializando conteúdo...