☕ 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

Mostrar mensagens com a etiqueta map. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta map. Mostrar todas as mensagens

sábado, 10 de junho de 2023

⚔️ YUUYA TENJOU E A DUNGEON DAS ARROW FUNCTIONS — QUANDO O COBOL ENCONTROU A SETA =>

 

Bellacosa Mainframe apresenta o Arrow Function e Lambda Functions

☕ Um Café no Bellacosa Mainframe

⚔️ YUUYA TENJOU E A DUNGEON DAS ARROW FUNCTIONS — QUANDO O COBOL ENCONTROU A SETA =>

Lambda, Arrow Functions, programação funcional, callbacks, map, filter, reduce, pipelines, legibilidade, carga cognitiva — e o dia em que um programador COBOL descobriu que x => x * 2 não era uma tentativa de economizar caracteres no teclado.



🎬 PRÓLOGO — YUUYA ENCONTROU UMA PORTA ESTRANHA

Todo programador possui uma porta para outro mundo.

Para Yuuya Tenjou, em Isekai de Cheat Skill, foi literalmente uma passagem para outro mundo.

Para um programador COBOL, às vezes basta abrir um programa JavaScript moderno.

Você entra inocentemente esperando encontrar variáveis, condições, loops e funções.

De repente aparece:

clientes
    .filter(c => c.ativo)
    .map(c => c.nome)
    .sort((a, b) => a.localeCompare(b));

O programador COBOL observa aquilo durante alguns segundos.

Depois mais alguns.

Finalmente pergunta:

— Cadê o IF?

Silêncio.

— Cadê o PERFORM?

Nada.

— Cadê o END-IF?

Apenas uma seta olhando para ele:

=>

Bem-vindo à Dungeon das Arrow Functions.

O objetivo deste artigo não é convencer você de que arrow functions são maravilhosas, nem dizer que programação funcional é superior ao COBOL.

Nosso objetivo é muito mais interessante:

entender por que essa maneira de programar existe, que problema ela resolve e, principalmente, como um programador acostumado ao pensamento COBOL pode aprender a lê-la rapidamente.

Pegue o café.

Yuuya abriu a porta.

Nós vamos entrar.



🏰 CAPÍTULO 1 — COBOL FOI FEITO PARA SER LIDO

Existe uma razão histórica para um programador COBOL estranhar JavaScript moderno.

Veja:

IF WS-IDADE >= 18
    DISPLAY 'ADULTO'
END-IF

Mesmo alguém com pouco conhecimento de COBOL consegue imaginar o que está acontecendo.

Há uma condição.

Há uma ação.

Há um encerramento explícito.

Compare com:

p => p.idade >= 18

A primeira impressão pode ser:

— Isso não foi construído para humanos.

Na realidade, foi construído para humanos que já dominam determinado vocabulário.

Esse detalhe é importante.

Quando você lê:

PERFORM VARYING IDX FROM 1 BY 1
    UNTIL IDX > WS-TOTAL

um iniciante também pode achar estranho.

Mas depois de anos trabalhando com COBOL, seu cérebro não interpreta individualmente:

PERFORM
VARYING
FROM
BY
UNTIL

Você reconhece um padrão inteiro:

LOOP.

É exatamente isso que precisa acontecer com:

x => x * 2

O objetivo não é interpretar cada símbolo.

O objetivo é reconhecer o padrão.



🧙 CAPÍTULO 2 — PRIMEIRA MAGIA DE YUUYA: => SIGNIFICA RECEBE E DEVOLVE

Vamos estabelecer nosso primeiro cheat skill.

Sempre que encontrar:

x => expressão

leia mentalmente:

RECEBE x E DEVOLVE expressão.

Portanto:

x => x * 2

vira:

recebe x e devolve x * 2.

Isso:

idade => idade >= 18

vira:

recebe idade e devolve verdadeiro se idade for maior ou igual a 18.

Isso:

cliente => cliente.ativo

vira:

recebe cliente e devolve o estado de cliente.ativo.

E:

(a, b) => a + b

significa:

recebe a e b e devolve a soma dos dois.

Pronto.

A seta perdeu boa parte da magia.



⚔️ CAPÍTULO 3 — LAMBDA NÃO É ARROW FUNCTION

Antes de avançarmos, precisamos separar dois conceitos.

Lambda é uma ideia.

Arrow function é uma sintaxe.

O conceito de funções lambda é muito anterior ao JavaScript moderno e está relacionado ao cálculo lambda desenvolvido por Alonzo Church na década de 1930.

O cálculo lambda tornou-se uma das bases teóricas da ciência da computação e influenciou profundamente linguagens funcionais.

JavaScript apenas oferece uma maneira compacta de escrever determinadas funções.

Compare:

function dobrar(numero) {
    return numero * 2;
}

com:

numero => numero * 2

A segunda forma é uma arrow function.

Conceitualmente ambas representam comportamento:

ENTRADA
   |
   V
numero
   |
   V
numero * 2
   |
   V
SAÍDA

A diferença principal aqui é a quantidade de cerimônia sintática.



🧠 CAPÍTULO 4 — POR QUE ALGUÉM QUIS DIMINUIR AS FUNÇÕES?

Imagine que precisamos filtrar clientes adultos.

Poderíamos escrever:

function clienteAdulto(cliente) {
    return cliente.idade >= 18;
}

const adultos = clientes.filter(clienteAdulto);

Isso é perfeitamente válido.

Mas talvez essa função seja usada somente naquele ponto.

Então podemos escrever:

const adultos =
    clientes.filter(cliente => cliente.idade >= 18);

A função foi criada exatamente onde é necessária.

Essa é uma das grandes vantagens das lambdas:

comportamentos pequenos podem ser definidos e passados diretamente para outras operações.

Em outras palavras, funções passam a ser tratadas como valores.

Podemos passar uma função para outra função.

Esse conceito recebe frequentemente o nome de funções de primeira classe.


📦 CAPÍTULO 5 — QUANDO O COMPORTAMENTO VIRA DADO

Aqui começa a parte realmente importante.

Um programador COBOL está acostumado com algo semelhante a:

PERFORM VALIDAR-CLIENTE

Existe uma rotina chamada VALIDAR-CLIENTE.

Agora imagine que pudéssemos entregar essa rotina para outra rotina dizendo:

Tome esta regra e execute-a para cada elemento.

É aproximadamente isso que acontece aqui:

clientes.filter(clienteAdulto);

Estamos entregando clienteAdulto para filter.

Mas também podemos criar a regra diretamente:

clientes.filter(
    cliente => cliente.idade >= 18
);

filter recebe comportamento.

Isso permite construir abstrações extremamente poderosas.


🏭 CAPÍTULO 6 — O COBOLZEIRO PENSA EM PERFORM

Imagine:

MOVE ZERO TO WS-CONTADOR

PERFORM VARYING IDX FROM 1 BY 1
    UNTIL IDX > WS-TOTAL

    IF CLIENTE-ATIVO(IDX) = 'S'
        ADD 1 TO WS-CONTADOR
    END-IF

END-PERFORM

Aqui estamos descrevendo explicitamente o mecanismo:

  1. inicializar;

  2. criar índice;

  3. incrementar índice;

  4. testar limite;

  5. acessar elemento;

  6. testar condição;

  7. executar ação.

Isso é pensamento imperativo.

Estamos dizendo:

computador, faça isso, depois aquilo, depois aquilo.

Agora observe:

clientes.filter(cliente => cliente.ativo);

Aqui estamos mais próximos de:

Dos clientes, quero aqueles que estão ativos.

Não especificamos explicitamente como percorrer a coleção.

filter já sabe fazer isso.

Nós fornecemos apenas a regra.

Essa diferença é fundamental.


🔮 CAPÍTULO 7 — IMPERATIVO VERSUS DECLARATIVO

Simplificando bastante:

Imperativo

Descreve como fazer.

PEGUE REGISTRO
TESTE
SE ACEITO
   MOVA
INCREMENTE
VOLTE

Declarativo

Descreve mais diretamente o que queremos.

FILTRAR CLIENTES ATIVOS

Programação funcional frequentemente permite aproximar o código dessa segunda maneira de pensar.

Isso não significa que uma seja universalmente melhor.

Significa que estamos trabalhando em níveis diferentes de abstração.


🗺️ CAPÍTULO 8 — O COPYBOOK SECRETO DAS FUNÇÕES FUNCIONAIS

Yuuya encontra agora um pergaminho escondido na dungeon.

Nele estão apenas sete palavras.

Memorize-as:

FILTER = QUEM FICA?

MAP = VIRA O QUÊ?

FIND = QUAL DELES?

SOME = EXISTE ALGUM?

EVERY = TODOS?

REDUCE = ACUMULA COMO?

SORT = COMPARA COMO?

Esse pequeno mapa muda completamente a maneira de ler JavaScript moderno.


🧹 CAPÍTULO 9 — FILTER: QUEM PASSA PELO PORTÃO?

Considere:

clientes.filter(
    cliente => cliente.idade >= 18
);

Não comece pela lambda.

Veja primeiro:

filter

Pergunte:

Quem fica?

Agora leia:

cliente => cliente.idade >= 18

Resposta:

clientes cuja idade seja maior ou igual a 18.

filter funciona como uma peneira.

Entrada:

Ana     15
Maria   27
João    17
Carlos  44

Regra:

IDADE >= 18

Saída:

Maria   27
Carlos  44

A lambda usada por filter normalmente produz:

TRUE

ou:

FALSE

TRUE passa.

FALSE fica para trás.


🪄 CAPÍTULO 10 — MAP: TRANSFORME UMA COISA EM OUTRA

Agora:

clientes.map(cliente => cliente.nome);

Pergunta:

Vira o quê?

Cada cliente vira seu nome.

Entrada:

[
    { nome: "Ana", idade: 30 },
    { nome: "Maria", idade: 41 }
]

Saída:

[
    "Ana",
    "Maria"
]

Pense:

CLIENTE --------> NOME
CLIENTE --------> NOME
CLIENTE --------> NOME

map é uma transformação.


🔎 CAPÍTULO 11 — FIND: ENCONTRE UM REGISTRO

clientes.find(
    cliente => cliente.id === 123
);

Leia:

encontre o cliente cujo ID seja 123.

Mentalmente, isso é muito próximo de procurar um registro por determinada condição.

O interessante é que não precisamos escrever explicitamente toda a lógica de iteração.


🧪 CAPÍTULO 12 — SOME E EVERY

Veja:

clientes.some(cliente => cliente.bloqueado);

Pergunta:

Existe algum cliente bloqueado?

Agora:

clientes.every(cliente => cliente.ativo);

Pergunta:

Todos os clientes estão ativos?

Esse vocabulário começa a tornar o código surpreendentemente expressivo depois que você aprende a lê-lo semanticamente.


👹 CAPÍTULO 13 — YUUYA ENCONTRA O BOSS: REDUCE

E finalmente chegamos ao monstro que assusta muita gente:

pedidos.reduce(
    (acc, pedido) => acc + pedido.valor,
    0
);

O COBOLzeiro olha:

— Agora vocês passaram dos limites.

Calma.

Vamos desmontar.

O 0 é o valor inicial.

Portanto:

ACC = 0

Depois:

(acc, pedido) => acc + pedido.valor

significa:

recebe acumulador e pedido e devolve acumulador + valor do pedido.

Em COBOL conceitualmente teríamos:

MOVE ZERO TO WS-TOTAL

PERFORM VARYING IDX FROM 1 BY 1
    UNTIL IDX > WS-QTD-PEDIDOS

    ADD PEDIDO-VALOR(IDX)
        TO WS-TOTAL

END-PERFORM

Portanto:

pedidos.reduce(
    (total, pedido) => total + pedido.valor,
    0
);

é essencialmente:

TOTAL = TOTAL + PEDIDO-VALOR

repetido para todos os pedidos.

O dragão morreu.


🏗️ CAPÍTULO 14 — O PODER REAL ESTÁ NO PIPELINE

Agora podemos juntar tudo:

const total =
    pedidos
        .filter(pedido => pedido.status === "PAGO")
        .map(pedido => pedido.valor)
        .reduce(
            (total, valor) => total + valor,
            0
        );

Não tente ler cada símbolo.

Primeiro veja:

PEDIDOS
   |
FILTER
   |
MAP
   |
REDUCE

Agora pergunte:

FILTER

Quem fica?

PEDIDOS PAGOS

MAP

Vira o quê?

PEDIDO → VALOR

REDUCE

Acumula como?

SOMA

Então o algoritmo inteiro significa:

Pegue os pedidos pagos, transforme-os em valores e some esses valores.


🖥️ CAPÍTULO 15 — ISSO PARECE MAIS MAINFRAME DO QUE VOCÊ IMAGINA

Podemos imaginar:

INPUT DATASET
      |
      V
    FILTER
      |
      V
 TRANSFORMATION
      |
      V
 AGGREGATION
      |
      V
   OUTPUT

Familiar?

Muito.

Um pipeline:

dados
    .filter(...)
    .map(...)
    .reduce(...)

pode ser mentalmente interpretado como uma sequência de etapas de processamento.

É quase como observar um fluxo batch.

Você primeiro identifica:

INPUT
STEP01
STEP02
STEP03
OUTPUT

Depois entra em cada etapa.

Essa é uma excelente estratégia para um programador mainframe.


📞 CAPÍTULO 16 — CALLBACK: TOME ESTA ROTINA E CHAME DEPOIS

Arrow functions aparecem muito em callbacks.

Veja:

setTimeout(() => {
    console.log("Olá");
}, 1000);

Primeiro:

() => {
    console.log("Olá");
}

significa:

função que não recebe parâmetros e imprime "Olá".

Agora:

setTimeout(FUNCAO, 1000);

significa aproximadamente:

execute essa função depois de 1000 milissegundos.

Poderíamos escrever:

function dizerOla() {
    console.log("Olá");
}

setTimeout(dizerOla, 1000);

A arrow function evita criar um nome para uma rotina utilizada apenas naquele ponto.


⚠️ CAPÍTULO 17 — MENOS CARACTERES NÃO SIGNIFICA MELHOR CÓDIGO

Agora Yuuya encontra uma armadilha.

Veja:

x => x.a > 10

Curto?

Sim.

Legível?

Depende.

O que é x?

O que é a?

Por que 10?

Compare:

cliente =>
    cliente.saldo > LIMITE_CREDITO

Muito melhor.

A arrow function não era o problema.

O problema eram os nomes.

Essa é uma lição extremamente importante.

Código compacto não é necessariamente código simples.


🧠 CAPÍTULO 18 — COMPRESSÃO DE CÓDIGO E DESCOMPRESSÃO CEREBRAL

Imagine:

x.filter(a=>a.b).map(c=>c.d).reduce((e,f)=>e+f,0)

Poucos caracteres.

Mas o cérebro precisa descobrir:

x = ?
a = ?
b = ?
c = ?
d = ?
e = ?
f = ?

Economizamos digitação e transferimos o custo para quem lê.

É como compactar o programa.

O desenvolvedor precisa executar mentalmente:

UNZIP ALGORITMO

antes de poder entendê-lo.

Código de produção vive muito mais tempo sendo lido do que sendo digitado.

Isso vale para COBOL.

Vale para JavaScript.

Vale para Java.

Vale para Python.

E valerá para a próxima linguagem da moda.


🧱 CAPÍTULO 19 — QUANDO NÃO USAR UMA LAMBDA GIGANTESCA

Isto é aceitável:

pedidos.filter(pedido => pedido.ativo);

Mas considere:

pedidos.filter(pedido =>
    pedido.ativo &&
    pedido.valor > 1000 &&
    pedido.cliente?.status !== "BLOQUEADO" &&
    pedido.itens.some(item => item.quantidade > 0)
);

Ainda funciona.

Mas talvez seja melhor escrever:

function pedidoElegivel(pedido) {
    return (
        pedido.ativo &&
        pedido.valor > 1000 &&
        pedido.cliente?.status !== "BLOQUEADO" &&
        pedido.itens.some(
            item => item.quantidade > 0
        )
    );
}

pedidos.filter(pedidoElegivel);

Agora o algoritmo principal diz:

filtre pedidos elegíveis.

A regra de elegibilidade está isolada.

Isso lembra muito:

PERFORM VALIDAR-PEDIDO

em vez de colocar 47 condições no meio do processamento principal.


🧭 CAPÍTULO 20 — O MÉTODO YUUYA PARA LER LAMBDAS

Encontrou:

usuarios
    .filter(u => u.ativo)
    .map(u => ({
        nome: u.nome,
        total: u.pedidos.reduce(
            (a, p) => a + p.valor,
            0
        )
    }))
    .filter(u => u.total > 1000);

Não entre em pânico.

Faça quatro passagens.

PASSO 1 — IDENTIFIQUE A ENTRADA

usuarios

Estamos trabalhando com usuários.

PASSO 2 — IGNORE AS LAMBDAS

Leia somente:

FILTER
MAP
FILTER

Já temos:

USUÁRIOS
   |
 FILTRAR
   |
TRANSFORMAR
   |
 FILTRAR

PASSO 3 — ABRA AS REGRAS EXTERNAS

Primeiro:

u => u.ativo

Somente usuários ativos.

Depois:

u => ({
    nome: u.nome,
    total: ...
})

Transforma usuário em:

NOME + TOTAL

Depois:

u => u.total > 1000

Mantém total superior a 1000.

PASSO 4 — ENTRE NA LAMBDA INTERNA

u.pedidos.reduce(
    (a, p) => a + p.valor,
    0
)

Tradução:

some os valores dos pedidos.

Resultado final:

USUÁRIOS
     |
     V
SOMENTE ATIVOS
     |
     V
CALCULAR NOME + TOTAL DOS PEDIDOS
     |
     V
TOTAL > 1000

Agora o código deixou de ser hieróglifo.


⚡ CAPÍTULO 21 — NÃO EXECUTE O PROGRAMA INTEIRO NA CABEÇA

Essa talvez seja a melhor dica deste artigo.

Programadores experientes não necessariamente entendem código complexo porque conseguem simular cinquenta instruções simultaneamente.

Eles reconhecem padrões.

Um operador de mainframe experiente olha:

IEFBR14

e não precisa analisar cada caractere.

Um programador COBOL olha:

PERFORM VARYING

e reconhece imediatamente um padrão.

Queremos construir o mesmo reflexo:

.filter(...)

→ seleção.

.map(...)

→ transformação.

.reduce(...)

→ agregação.

Depois disso, sua velocidade de leitura aumenta enormemente.


🎒 CAPÍTULO 22 — A DUNGEON TEM NÍVEIS DE ABSTRAÇÃO

Uma das grandes mudanças da programação moderna é o aumento do nível de abstração.

Antigamente poderíamos escrever:

PEGUE ELEMENTO
COMPARE
INCREMENTE
COPIE
VOLTE

Hoje escrevemos:

filter(...)

Isso não significa que o loop desapareceu.

Ele foi encapsulado.

Esse conceito é importantíssimo.

Abstração não elimina trabalho.

Abstração esconde implementação atrás de uma interface.

Quando fazemos:

SELECT *
FROM CLIENTES
WHERE ATIVO = 'S';

também não especificamos como o banco deve percorrer fisicamente cada página.

O SGBD resolve.

Um COBOLzeiro já trabalha com abstrações há décadas.

Talvez apenas não chamasse dessa maneira.


📜 CAPÍTULO 23 — CURIOSIDADE: COBOL TAMBÉM É UMA GRANDE ABSTRAÇÃO

Imagine explicar para um programador Assembly de décadas atrás:

ADD WS-VALOR TO WS-TOTAL

Ele poderia responder:

— Preguiçosos! Cadê os registradores?

😂

Toda geração acha que a próxima está escondendo detalhes demais.

Assembly abstraiu código de máquina.

COBOL abstraiu muitos detalhes da máquina.

SQL abstraiu mecanismos de acesso aos dados.

Bibliotecas abstraíram algoritmos.

Frameworks abstraíram infraestrutura.

filter, map e reduce abstraem padrões de iteração.

A história da programação é, em boa medida, a história da criação de abstrações.


🥚 EASTER EGG — 03:17 NA DUNGEON

Às 03:17, Yuuya encontra um programa:

const incidente =
    eventos
        .filter(evento => evento.critico)
        .find(evento => evento.hora === "03:17");

O operador pergunta:

— Encontrou a causa?

Yuuya responde:

— Não. Só encontrei correlação.

O veterano do mainframe sorri.

Finalmente alguém aprendeu troubleshooting.

Porque:

CORRELAÇÃO != CAUSALIDADE

Nem mesmo uma arrow function possui Cheat Skill suficiente para mudar isso.


🧰 CAPÍTULO 24 — EXERCÍCIO PARA TREINAR SEU CÉREBRO COBOL

Pegue:

transacoes
    .filter(t => t.aprovada)
    .map(t => t.valor)
    .reduce((total, valor) => total + valor, 0);

Não traduza sintaxe.

Faça:

ENTRADA:
TRANSAÇÕES

FILTER:
QUEM FICA?

APROVADAS

MAP:
VIRA O QUÊ?

VALOR

REDUCE:
ACUMULA COMO?

SOMA

Conclusão:

some o valor de todas as transações aprovadas.

Agora imagine o equivalente COBOL:

MOVE ZERO TO WS-TOTAL

PERFORM VARYING IDX FROM 1 BY 1
    UNTIL IDX > WS-QTD-TRANSACOES

    IF TRANSACAO-APROVADA(IDX) = 'S'
        ADD TRANSACAO-VALOR(IDX)
            TO WS-TOTAL
    END-IF

END-PERFORM

São dois estilos descrevendo essencialmente a mesma intenção.


🏆 CAPÍTULO 25 — QUANDO ARROW FUNCTIONS REALMENTE BRILHAM

Elas são especialmente convenientes quando a função é:

  • pequena;

  • local;

  • usada uma única vez;

  • semanticamente evidente;

  • passada como callback;

  • utilizada em transformação de coleções.

Por exemplo:

precos.map(preco => preco * 1.10);

Perfeitamente razoável.

Mas quando a lógica cresce, nomes podem ser superiores.

Em vez de:

clientes.filter(c =>
    c.a &&
    c.b &&
    !c.x &&
    c.y > 500
);

prefira algo como:

clientes.filter(clienteElegivel);

O nome carrega significado.


🧙 CAPÍTULO 26 — A REGRA DE OURO DE YUUYA

Depois de atravessar a dungeon, Yuuya escreve na parede:

Não tente entender a seta. Entenda o fluxo dos dados.

Essa é a mudança fundamental.

Diante de:

dados
    .filter(...)
    .map(...)
    .reduce(...)

pergunte:

DE ONDE VÊM OS DADOS?

QUEM SOBREVIVE?

NO QUE SE TRANSFORMAM?

COMO SÃO AGREGADOS?

QUAL É A SAÍDA?

Essas perguntas funcionam quase independentemente da linguagem.


☕ EPÍLOGO — O COBOLZEIRO VOLTA DA DUNGEON

Yuuya atravessou a porta para outro mundo e descobriu habilidades que inicialmente pareciam mágicas.

O programador COBOL pode passar por experiência semelhante quando encontra programação funcional moderna.

No começo:

x => x * 2

parece uma linguagem inventada por alguém que cobrava por caractere.

Depois entendemos:

RECEBE X
DEVOLVE X * 2

Então encontramos:

filter

e aprendemos:

QUEM FICA?

Encontramos:

map

e perguntamos:

VIRA O QUÊ?

Encontramos:

reduce

e finalmente percebemos:

ACUMULA COMO?

A dungeon deixa de parecer tão assustadora.

Arrow functions não foram criadas porque funções tradicionais estavam erradas.

Elas surgiram porque determinadas situações envolvem pequenos comportamentos usados localmente, especialmente callbacks e transformações de coleções, e escrever toda a cerimônia de uma função nomeada repetidamente pode tornar o código desnecessariamente verboso.

Mas existe uma lição ainda mais importante.

Concisão não é sinônimo de legibilidade.

Isto:

x.filter(a=>a.b).map(c=>c.d).reduce((e,f)=>e+f,0)

pode impressionar alguém pela quantidade de lógica comprimida em uma linha.

Mas software corporativo não é campeonato de quem digita menos.

O programa que você escreve hoje poderá ser investigado durante um incidente às 03:17 daqui a quinze anos.

Talvez por alguém que ainda nem entrou na empresa.

Talvez por um jovem programador.

Talvez por uma IA.

Talvez por você mesmo, tentando lembrar:

"Quem foi o animal que escreveu isso?"

E então o Git responde:

AUTHOR: VOCÊ
DATE: 2026

Nesse momento você compreenderá definitivamente por que nomes importam.

COBOL possui algo valioso para ensinar às linguagens modernas: software também é comunicação entre seres humanos.

Programação funcional possui algo igualmente valioso para ensinar ao COBOLzeiro: nem sempre precisamos descrever cada passo mecânico de uma transformação quando podemos expressar diretamente sua intenção.

O verdadeiro Cheat Skill não é decorar:

=>

É aprender a reconhecer:

ENTRADA
   |
   V
SELEÇÃO
   |
   V
TRANSFORMAÇÃO
   |
   V
AGREGAÇÃO
   |
   V
SAÍDA

Quando você começa a enxergar isso, deixa de tentar executar cada instrução mentalmente.

Você passa a enxergar a arquitetura do algoritmo.

E então aquela linha:

pedidos
    .filter(pedido => pedido.pago)
    .map(pedido => pedido.valor)
    .reduce((total, valor) => total + valor, 0);

deixa de parecer uma magia de outro mundo.

Você olha para ela e pensa:

PEDIDOS
  ↓
PAGOS
  ↓
VALORES
  ↓
SOMA

Yuuya guarda a espada.

O COBOLzeiro termina o café.

E a misteriosa porta => permanece aberta.

Desta vez, porém, não existe mais monstro do outro lado.

Existe apenas outra maneira de escrever algoritmos.


☕ COPYBOOK FINAL DO BELLACOSA MAINFRAME

Se quiser guardar apenas uma página deste artigo, guarde esta:

=>      RECEBE → DEVOLVE

FILTER  QUEM FICA?

MAP     VIRA O QUÊ?

FIND    QUAL DELES?

SOME    EXISTE ALGUM?

EVERY   TODOS?

REDUCE  ACUMULA COMO?

SORT    COMPARA COMO?

E diante de qualquer pipeline funcional:

1. DESCUBRA A ENTRADA.

2. LEIA SOMENTE OS NOMES DAS OPERAÇÕES.

3. IDENTIFIQUE O FLUXO DOS DADOS.

4. ABRA UMA LAMBDA DE CADA VEZ.

5. TROQUE => POR "RECEBE E DEVOLVE".

6. DÊ NOMES HUMANOS ÀS VARIÁVEIS.

7. SE A LAMBDA FICOU GRANDE DEMAIS,
   EXTRAIA UMA FUNÇÃO COM NOME.

8. NÃO EXECUTE O PROGRAMA INTEIRO
   NA CABEÇA.

9. RECONHEÇA PADRÕES.

10. LEMBRE-SE:
    MENOS CARACTERES != MENOS COMPLEXIDADE.

No fim das contas, seja em COBOL, JavaScript, Java, Python ou no próximo isekai tecnológico, continuamos fazendo a mesma coisa:

recebendo dados, tomando decisões, transformando informações e produzindo resultados.

A sintaxe muda.

A dungeon muda.

Os monstros ganham nomes novos.

Mas o algoritmo continua lá.

E um programador que aprende a enxergá-lo por trás da sintaxe já desbloqueou seu verdadeiro Cheat Skill.

terça-feira, 10 de agosto de 2021

REDEFINES, Compiler Options e o Diagnóstico que o COBOL Iniciante Não Vê

 

Bellacosa Mainframe e o redefines em cobol risco de perigo eminente

☕ Um Café no Bellacosa Mainframe

REDEFINES, Compiler Options e o Diagnóstico que o COBOL Iniciante Não Vê

🩺 House M.D. entra no CPD: “O compilador não mente. O programador é que perguntou a coisa errada.”

Existe um momento inevitável na vida de todo programador COBOL.

Você está diante de um programa aparentemente simples.

O JCL compilou.

O retorno foi bonito.

MAXCC=0

Nenhuma explosão.

Nenhum abend.

Nenhum operador telefonando.

Nenhum gerente atravessando o corredor com aquela expressão típica de quem acabou de descobrir que o processamento noturno está duas horas atrasado.

Você olha para o código e pensa:

— Funcionou.

E, em algum lugar do hospital Princeton-Plainsboro do mainframe, o Dr. Gregory House manca pelo corredor, toma um Vicodin imaginário e responde:

— Não. Apenas ainda não morreu.

Bem-vindo ao Bellacosa Mainframe.

Hoje vamos investigar um daqueles problemas que parecem pequenos demais para merecer atenção até o dia em que deixam de ser pequenos:

REDEFINES com tamanhos diferentes.

E, a partir dele, vamos abrir uma porta muito maior:

  • como o COBOL organiza memória;

  • o que REDEFINES realmente faz;

  • por que o compilador pode aceitar coisas que você não deveria escrever;

  • como identificar diferenças de tamanho;

  • como usar MAP;

  • como usar XREF;

  • quais opções de compilação ajudam;

  • onde essas opções podem ser configuradas;

  • o que é CBL;

  • o que é PROCESS;

  • o que é SYSOPTF;

  • o que é OPTFILE;

  • o que é IGYCDOPT;

  • como funciona a precedência das opções;

  • e por que um bom programador COBOL precisa aprender a ler o listing como um médico lê um exame de sangue.

Pegue o café.

Temos um paciente.



🩻 Caso clínico: o REDEFINES suspeito

Imagine o seguinte código:

       WORKING-STORAGE SECTION.

       01  WS-AREA-ORIGINAL.
           05 WS-CAMPO-A        PIC X(10).
           05 WS-CAMPO-B        PIC X(10).

       01  WS-AREA-NOVA
           REDEFINES WS-AREA-ORIGINAL.
           05 WS-CAMPO-C        PIC X(30).

O iniciante olha rapidamente.

Área original:

10 + 10 = 20 bytes

Área redefinida:

30 bytes

Logo nasce a pergunta:

“Como uma área de 30 bytes pode redefinir uma área que aparentemente tem 20?”

Excelente pergunta.

E é exatamente aí que mora a armadilha.



🧠 Primeiro diagnóstico: REDEFINES não significa “nova variável”

Talvez o erro conceitual mais comum seja imaginar que isto:

01 A PIC X(10).
01 B REDEFINES A PIC X(10).

significa:

A = 10 bytes
B = 10 bytes
TOTAL = 20 bytes

Não.

Pense em uma gaveta.

Existe uma única gaveta física.

Você pode colocar uma etiqueta nela escrito:

A

ou outra etiqueta:

B

Mas continua sendo a mesma gaveta.

Conceitualmente:

             MEMÓRIA

      ┌──────────────────┐
A --> │ A B C D E F G H I J │
      └──────────────────┘
               ↑
B -------------┘

A e B são duas maneiras diferentes de interpretar os mesmos bytes.

Então grave isto:

REDEFINES cria uma nova visão lógica, não necessariamente uma nova área independente de storage.

Uma frase Bellacosa para decorar:

REDEFINES não constrói outro apartamento. Ele entrega outra planta do mesmo apartamento.



🏠 Um exemplo clássico e correto

Datas são perfeitas para entender isso.

       01  WS-DATA.
           05 WS-DATA-INTEIRA   PIC X(08).

       01  WS-DATA-DETALHE
           REDEFINES WS-DATA.
           05 WS-ANO            PIC 9(04).
           05 WS-MES            PIC 9(02).
           05 WS-DIA            PIC 9(02).

Se WS-DATA-INTEIRA contiver:

20260816

a mesma sequência pode ser visualizada assim:

WS-DATA-INTEIRA

┌─────────────────────────┐
│ 2 0 2 6 0 8 1 6         │
└─────────────────────────┘

ou:

WS-DATA-DETALHE

┌────────────┬──────┬──────┐
│ WS-ANO     │ MÊS  │ DIA  │
│ 2026       │ 08   │ 16   │
└────────────┴──────┴──────┘

Mesmos oito bytes.

Duas interpretações.

Perfeito.


🚨 Então o tamanho precisa ser sempre igual?

Aqui começa a parte interessante.

Para ensinar um iniciante, eu usaria uma regra extremamente conservadora:

TAMANHO DO REDEFINES
        =
TAMANHO DA ÁREA REDEFINIDA

Essa regra evita muitos erros.

Mas o COBOL real é mais complexo.

Dependendo do nível da estrutura, da versão do compilador, das regras da linguagem e do Enterprise COBOL utilizado, algumas redefinições com comprimentos diferentes podem ser aceitas.

Ou seja:

O compilador aceitar não significa que o design seja bom.

House provavelmente escreveria no quadro:

COMPILES ≠ CORRECT

E depois apagaria metade da palavra só para irritar a equipe.


🧪 O erro realmente perigoso

O problema não é simplesmente escrever:

05 B REDEFINES A PIC X(30).

O problema é escrever isso sem perceber as implicações.

Porque daí temos uma diferença enorme entre duas perguntas:

Pergunta errada

“O compilador aceitou?”

Pergunta certa

“O layout de memória resultante é exatamente aquele que eu acredito que seja?”

A segunda pergunta separa quem está apenas digitando COBOL de quem está começando a entender COBOL.


🔬 Não confie apenas nos olhos: use o MAP

Aqui aparece um dos recursos mais úteis e menos apreciados pelos iniciantes:

MAP

Ao compilar com MAP, o Enterprise COBOL produz informações sobre o layout da DATA DIVISION.

Em outras palavras:

o compilador mostra como ele entendeu seus campos.

Isso é quase um raio-X da memória.

Imagine:

       01 WS-REGISTRO.
          05 WS-NOME     PIC X(20).
          05 WS-IDADE    PIC 99.
          05 WS-SALARIO  PIC S9(7)V99 COMP-3.

Você pode acreditar que sabe exatamente como isso está organizado.

Mas o listing é a resposta do compilador.

E sempre que houver estruturas complexas com:

REDEFINES
OCCURS
COMP
COMP-3
SYNC
SIGN
GROUP ITEMS
COPYBOOKS

a leitura do mapa se torna extremamente valiosa.


🩻 MAP é o raio-X; SOURCE é a ficha clínica

Eu normalmente colocaria em desenvolvimento algo semelhante a:

MAP
SOURCE
XREF(FULL)

Cada opção responde uma pergunta diferente.

SOURCE

Você vê o fonte associado ao listing.

Isso facilita conectar mensagens, offsets e referências ao código que realmente foi compilado.

Parece trivial.

Até você descobrir que o programa que estava olhando não era exatamente o programa que entrou na compilação.

Copybooks entram.

Pré-compiladores entram.

Conditional compilation entra.

E de repente:

“Mas no meu fonte não está assim!”

Bem-vindo ao mainframe.

O compilador não lê suas intenções.

Ele lê aquilo que efetivamente recebeu.


🧬 XREF(FULL): quem mexeu no paciente?

XREF significa referência cruzada.

Imagine que você encontra:

WS-CUSTOMER-AREA

e quer saber:

  • onde foi declarada;

  • onde foi usada;

  • quem altera;

  • quem consulta;

  • quais parágrafos fazem referência a ela.

É aí que:

XREF(FULL)

se torna extremamente útil.

Ele não é especificamente um detector mágico de REDEFINES incorreto.

Mas responde uma pergunta fundamental na investigação:

“Quem está usando esta área?”

House chamaria isso de procurar quem teve contato com o paciente antes dos sintomas aparecerem.


🧯 Regra prática para o iniciante

Sempre que você encontrar:

REDEFINES

pare.

Faça quatro perguntas:

1. Qual é o tamanho da área original?

2. Qual é o tamanho da redefinição?

3. Essa diferença é intencional?

4. Eu conferi o MAP?

Essa pausa de trinta segundos pode economizar horas de debugging.


🧮 Não confie demais na contagem visual

Isto parece fácil:

05 CAMPO-A PIC X(10).
05 CAMPO-B PIC X(10).

Total:

20

Mas layouts reais podem envolver:

05 VALOR-A PIC S9(9) COMP.
05 VALOR-B PIC S9(7)V99 COMP-3.
05 TABELA OCCURS 15 TIMES.

Agora contar “na cabeça” começa a ficar menos divertido.

E podemos adicionar:

SYNC
USAGE
SIGN
OCCURS DEPENDING ON
nested groups
copybooks

Pronto.

Você não está mais contando caracteres.

Está fazendo arqueologia.


🗿 Easter egg número 1: Indiana Jones e o Copybook Perdido

Todo programador COBOL experiente já encontrou algo assim:

COPY ABCD001.

Você abre.

Dentro existe:

COPY ABCD002.

Abre o segundo.

Existe:

COPY ABCD003.

Quinze minutos depois você está procurando um copybook criado em 1989 por um programador chamado Geraldo que se aposentou antes do Windows 95.

Indiana Jones tinha menos trabalho.


🧪 Existe warning para REDEFINES?

A resposta precisa ser cuidadosamente entendida.

O Enterprise COBOL possui diversas opções de diagnóstico e comportamento, incluindo regras relacionadas a construções da linguagem.

Uma opção interessante é a família:

RULES

e, dentro dela, configurações relacionadas à permissividade de redefinições.

Por exemplo:

RULES(NOLAXREDEF)

pode tornar determinadas situações de REDEFINES mais rigorosas.

Mas atenção:

não pense em NOLAXREDEF como um “detector universal de REDEFINES maior”.

Isso seria simplificar demais.

Ele atua sobre regras específicas da linguagem e situações de redefinição que o compilador poderia tratar de maneira mais permissiva.

Então a estratégia profissional não deve ser:

“Coloquei NOLAXREDEF, estou protegido.”

Deve ser:

compiler diagnostics
+
MAP
+
coding standard
+
static analysis
+
code review

Defesa em profundidade.

Segurança da informação descobriu isso faz décadas.

COBOL também merece.


🏥 Diagnóstico diferencial

House raramente acreditava na primeira hipótese.

Você também não deveria.

Se um programa possui corrupção de campos, valores estranhos ou informações que aparecem “do nada”, REDEFINES é apenas uma das hipóteses.

Também investigue:

MOVE com tamanhos incompatíveis
subscripts incorretos
índices fora do limite
OCCURS
OCCURS DEPENDING ON
reference modification
COMP-3 inválido
campos numéricos contendo lixo
copybook incompatível
arquivo com LRECL errado
layout diferente entre produtor e consumidor
LINKAGE SECTION incorreta
CALL com assinatura incompatível
CICS COMMAREA
MQ payload
VSAM
Db2 host variables

O sintoma pode aparecer em um campo.

A doença pode estar cinquenta linhas antes.

Ou cinquenta programas antes.


🧰 Minha configuração de investigação

Para ambiente de desenvolvimento, algo nessa filosofia é ótimo:

SOURCE
MAP
XREF(FULL)

e opções adicionais de diagnóstico conforme a política da instalação.

Não estou dizendo:

“Use exatamente esse conjunto em qualquer empresa.”

Cada shop possui padrões, versões, custos de listing, ferramentas, pipelines e políticas.

Mas o princípio é sólido:

Durante desenvolvimento, peça ao compilador para mostrar o máximo possível sobre o que ele entendeu.


🔧 “Mas onde coloco esses parâmetros?”

Agora chegamos a outra pergunta fundamental.

Muitos iniciantes acreditam que as opções de compilação existem apenas no JCL:

//COBOL EXEC PGM=IGYCRCTL,
// PARM='MAP,XREF,SOURCE'

Não.

O JCL é apenas uma das portas.

E entender isso muda completamente sua visão sobre compilação COBOL.


🚪 Porta 1 — PARM no JCL

A forma mais conhecida:

//COB EXEC PGM=IGYCRCTL,
// PARM='MAP,XREF(FULL),SOURCE'

Ou por meio de uma PROC corporativa.

É simples.

É visível.

E funciona bem.

Mas existe uma fraqueza:

depende do ambiente de compilação utilizado.

Se alguém usar outra PROC, outro pipeline ou outro processo, aquelas opções podem mudar.


🚪 Porta 2 — CBL

Você pode colocar opções diretamente no fonte.

Exemplo:

       CBL MAP,XREF(FULL),SOURCE
       IDENTIFICATION DIVISION.
       PROGRAM-ID. PACIENTE.

Isso significa:

“Este programa solicita essas opções.”

É muito poderoso porque a configuração viaja junto com o fonte.


🚪 Porta 3 — PROCESS

Outra sintaxe:

       PROCESS MAP,XREF(FULL),SOURCE
       IDENTIFICATION DIVISION.

Conceitualmente, PROCESS e CBL servem para informar opções ao compilador a partir do próprio programa.

Existe uma regra importante:

essas diretivas aparecem no início apropriado do fonte.

Elas não são statements executáveis.

Você não coloca:

PROCEDURE DIVISION.
    PROCESS MAP.

Isso seria como tentar mudar a configuração do aparelho de raio-X depois que o paciente já voltou para casa.


🧳 Quando usar CBL ou PROCESS?

Imagine uma aplicação que, por característica própria, precisa sempre ser compilada com determinada configuração.

Nesse caso faz sentido considerar:

CBL / PROCESS

Mas existe uma discussão arquitetural importante.

Você quer que:

cada programa decida suas próprias opções

ou:

a empresa defina um padrão central?

Essa decisão importa.

Porque programas vivem décadas.

Padrões corporativos mudam.

Compiladores mudam.

Ambientes mudam.


🚪 Porta 4 — SYSOPTF e OPTFILE

Agora entramos numa solução muito interessante para shops organizadas.

Em vez de escrever quarenta opções no JCL, você pode manter um arquivo de opções.

Algo conceitualmente assim:

EMPRESA.COBOL.OPTIONS(DEV)

contendo:

MAP
SOURCE
XREF(FULL)

E associá-lo como:

//SYSOPTF DD DSN=EMPRESA.COBOL.OPTIONS(DEV),DISP=SHR

O compilador pode então consumir essas opções através do mecanismo apropriado de option file.

Isso permite criar perfis.

Por exemplo:

EMPRESA.COBOL.OPTIONS(DEV)

EMPRESA.COBOL.OPTIONS(TEST)

EMPRESA.COBOL.OPTIONS(PROD)

EMPRESA.COBOL.OPTIONS(DEBUG)

EMPRESA.COBOL.OPTIONS(MIGRATION)

Isso é muito mais elegante do que copiar e colar parâmetros em dezenas de PROCs.


🧠 Easter egg número 2: House odeia copy & paste

Se House fosse tech lead COBOL, provavelmente diria:

— Você copiou quarenta opções de compilação de um JCL de 2017 sem saber o que fazem?

— Sim.

— Então não temos um bug. Temos uma religião.


🚪 Porta 5 — IGYCDOPT

Agora chegamos ao porão do hospital.

Lugar onde iniciantes raramente entram.

O Enterprise COBOL possui defaults de instalação.

Um dos nomes importantes nesse universo é:

IGYCDOPT

É nele que a organização pode estabelecer defaults para o compilador.

Pense assim:

programador
   |
   v
programa COBOL
   |
   v
PROC/JCL
   |
   v
Enterprise COBOL
   |
   v
defaults da instalação

O IGYCDOPT permite que a instalação diga:

“Se ninguém especificar nada diferente, use isto.”

E certas opções podem ainda ser controladas de modo que não sejam livremente sobrescritas.

É aí que recomendação vira governança.


🏛️ Política versus preferência

Existe uma diferença gigante entre:

“Recomendamos usar esta opção.”

e:

“A instalação está configurada desta maneira.”

Na primeira:

João usa.

Maria esquece.

Pedro usa outra PROC.

Carlos compila no pipeline antigo.

Na segunda:

a infraestrutura ajuda a garantir consistência.

Esse princípio aparece em tudo:

RACF
DevSecOps
Git
pipeline
quality gates
compiler defaults

Se algo é realmente importante, tente automatizar.


🧭 Mas quem ganha quando existem várias opções?

Excelente pergunta.

Imagine:

instalação:

NOMAP

JCL:

MAP

fonte:

CBL NOMAP

Qual vale?

Isso nos leva à:

⚔️ PRECEDÊNCIA DAS OPÇÕES

Você precisa entender que opções podem ser fornecidas por diferentes origens.

Conceitualmente:

INSTALLATION DEFAULTS
        ↓
INVOCATION / JCL
        ↓
PROCESS / CBL

mas existem particularidades, exceções e opções que podem ser fixadas pela instalação.

Então nunca faça debugging baseado apenas em:

“Eu tenho certeza de que a PROC usa MAP.”

Abra o listing.

Veja:

OPTIONS IN EFFECT

O compilador costuma informar quais opções efetivamente estavam ativas.

Essa seção vale ouro.


🔍 Regra Bellacosa número 1

Não pergunte qual opção estava no JCL. Pergunte qual opção estava em efeito.

Existe uma diferença brutal.

O JCL representa intenção.

O listing representa execução.


🧾 Leia o listing!

Aqui está talvez a maior dica deste artigo.

Programador iniciante pensa que o listing serve para:

achar erro de compilação

Programador intermediário usa para:

achar warning

Programador experiente usa o listing como:

documentação do programa compilado

Ele procura:

compiler version
compiler options
messages
data map
cross reference
code generation
statistics
source expansion

Você começa olhando um erro.

Termina entendendo como o compilador enxerga seu programa.


🩸 MAXCC=0 é apenas pressão arterial normal

Este merece moldura.

Você recebeu:

MAXCC=0

Parabéns.

Isso significa apenas que aquele job terminou com aquela condição.

Não significa:

lógica correta
layout correto
regra de negócio correta
performance boa
thread safety
dados corretos
segurança correta
arquivo correto
transação correta

House olha o monitor cardíaco.

Paciente vivo.

Ele não diz:

— Curado.

Ele pergunta:

— Por que ele desmaiou?

Faça o mesmo.


🔬 Passo a passo para investigar um REDEFINES

Encontrou:

REDEFINES

Faça isto.

Passo 1 — identifique a área original

Exemplo:

01 WS-ORIGINAL.
   05 A PIC X(10).
   05 B PIC X(10).

Calcule:

20 bytes

Passo 2 — identifique todas as redefinições

Pode existir mais de uma:

01 WS-TEXTO REDEFINES WS-ORIGINAL.
...

01 WS-NUMERICO REDEFINES WS-ORIGINAL.
...

01 WS-FLAGS REDEFINES WS-ORIGINAL.
...

Não pare na primeira.


Passo 3 — calcule os comprimentos

Faça isso cuidadosamente.

Principalmente se houver:

COMP
COMP-3
OCCURS
nested groups

Passo 4 — procure a intenção

Pergunte:

“Por que alguém criou este REDEFINES?”

Talvez seja:

header/body
mensagem de vários tipos
record type
layout de arquivo
estrutura histórica
flags
data
packed decimal
interface externa

Nem todo código estranho está errado.

Às vezes existe uma razão escrita em 1994.

Às vezes não.


🗿 Easter egg número 3: comentário de 1994

Você encontra:

*> NÃO ALTERAR ESTA ÁREA

Sem explicação.

Sem nome.

Sem ticket.

Sem documentação.

Naturalmente você pensa:

— Por quê?

A resposta está enterrada em uma fita magnética enviada para Iron Mountain durante o governo Itamar Franco.

Não altere.


🧪 Passo 5 — compile com MAP

Veja offsets.

Veja comprimentos.

Veja como o compilador efetivamente montou a área.

Nunca deixe a teoria vencer o mapa real.


🔍 Passo 6 — consulte o XREF

Descubra quem usa:

área original
área redefinida
campos individuais

Isso ajuda muito a entender o impacto.


🧬 Passo 7 — procure interfaces

Essa área vai para algum lugar?

WRITE
CALL
EXEC CICS LINK
EXEC CICS XCTL
EXEC CICS SEND
EXEC CICS RECEIVE
MQPUT
MQGET

Ou talvez:

arquivo VSAM
arquivo sequencial
Db2
IMS
socket
API
z/OS Connect

A área interna pode ter trinta bytes.

A interface externa pode esperar vinte.

Agora temos um problema de verdade.


💣 O pior cenário: contratos de dados

Imagine:

Programa A produz:

20 bytes

Programa B acredita que recebe:

30 bytes

Programa C possui um copybook antigo com:

24 bytes

Todos compilam.

Todos possuem:

MAXCC=0

House sorri.

— Finalmente um caso interessante.

Isso é exatamente o tipo de problema que aparece em ambientes legados.

O bug não está em um programa.

Está no contrato invisível entre programas.


🧠 Por isso copybook é tão importante

Copybooks não são apenas uma conveniência para evitar repetição.

Eles funcionam como contratos compartilhados de dados.

Quando bem utilizados:

Programa A ─┐
Programa B ─┼── COPY CUSTOMER
Programa C ─┘

todos enxergam a mesma definição.

Mas existe outro perigo:

versões diferentes do mesmo copybook

Programa A compilado ontem.

Programa B compilado seis anos atrás.

O dataset do copybook foi atualizado.

O módulo load de B continua com o layout antigo.

Agora você possui duas verdades.

Uma no fonte.

Outra no executável.

Bem-vindo novamente ao mainframe.


🔦 Static analysis: não espere um humano perceber

Aqui está uma excelente regra automática:

FOR EACH REDEFINES

compare
LENGTH(original)
with
LENGTH(redefinition)

Se:

=

normal.

Se:

<>

alerta.

Se:

redefinition > original

alerta forte.

Não porque todo caso seja necessariamente inválido.

Mas porque todo caso merece explicação.

Esse é o princípio de uma boa ferramenta estática:

destacar o que é incomum o bastante para merecer revisão humana.


🤖 Code review aumentado por IA

Aqui está uma aplicação interessante para IA em mainframe.

Imagine um pipeline lendo COBOL e produzindo:

WARNING COBOL-RD-001

WS-TRANSACTION-V2
redefines
WS-TRANSACTION

Original logical length: 128
Redefinition length: 136

Difference: +8 bytes

Review recommended.

Depois correlaciona com:

CALL USING
CICS COMMAREA
COPYBOOK
MQ
arquivo

Agora estamos usando IA não para substituir o programador.

Estamos usando IA para apontar:

“Ei, House. Esse exame aqui está estranho.”


🧯 Regra Bellacosa número 2

Nunca trate warning como decoração.

Compile listings antigos e você encontrará:

W
W
W
W
W
W
W

O programa está em produção há quinze anos.

Todo mundo diz:

— Sempre funcionou.

Essa é possivelmente uma das frases mais perigosas da informática.


🐸 “Sempre funcionou”

Uma aplicação COBOL antiga pode ter sobrevivido:

mudanças de hardware
mudanças de compilador
mudanças de sistema operacional
mudanças de middleware
novas versões de Db2
novas versões de CICS
mudanças de encoding
migrações
novos copybooks

Até que alguém recompila.

Então aparece:

“Funcionava antes.”

Talvez.

Talvez apenas estivesse dependendo de um comportamento antigo.


🧬 Compiler migration é medicina geriátrica

Migrar COBOL 4 para COBOL 6, por exemplo, não é simplesmente:

recompile

Você está trazendo código escrito sob determinadas hipóteses para outro ambiente.

Coisas que passaram despercebidas por décadas podem emergir.

Por isso opções de diagnóstico, listings, testes e análise estática são tão importantes.


🔐 Onde eu colocaria as opções?

Aqui entra arquitetura de engenharia.

Para um programa específico

Considere:

CBL
PROCESS

quando fizer sentido que aquela configuração acompanhe o fonte.


Para um projeto

Pode fazer sentido:

SYSOPTF
option file

Para toda a empresa

Pode fazer sentido usar:

defaults de instalação
IGYCDOPT

Para uma execução específica

Use:

PARM no JCL

Isso cria quatro níveis mentais:

PROGRAMA
PROJETO
PIPELINE
EMPRESA

Uma arquitetura muito melhor do que:

“coloca esse PARM aí e vê se funciona.”

🏗️ Um padrão de shop possível

Imagine:

           CORPORATIVO
              |
          IGYCDOPT
              |
     -------------------
     |                 |
  projeto A         projeto B
     |                 |
 SYSOPTF           SYSOPTF
     |                 |
   PROC              PROC
     |                 |
 programa           programa
 CBL/PROCESS        CBL/PROCESS

Agora você consegue controlar:

defaults
exceções
projetos
programas especiais

Isso é engenharia.


🚨 Não transforme opção de compilador em superstição

Outro erro comum:

“Fulano disse para usar SSRANGE.”

Então alguém coloca:

SSRANGE

sem saber o que ela faz.

Depois alguém copia.

Depois vira padrão.

Dez anos depois ninguém sabe por quê.

Toda opção de compilador deveria possuir pelo menos:

nome
objetivo
impacto
ambiente
quando usar
quando não usar

📚 Crie um catálogo corporativo

Algo simples:

OPTION: MAP

Uso:
Development / Migration

Objetivo:
Mostrar layout da Data Division.

Obrigatória:
DEV = SIM
PROD = opcional

Responsável:
Mainframe Engineering

Faça isso para:

MAP
XREF
SOURCE
SSRANGE
NUMCHECK
OPTIMIZE
ARCH
TEST
LIST
OFFSET

Seu eu do futuro agradecerá.


🧠 Easter egg número 4: Differential Diagnosis

Quando House recebe um paciente, a equipe lista hipóteses no quadro.

Faça exatamente isso no debugging.

Sintoma:

WS-VALOR contém lixo

Quadro:

REDEFINES?
MOVE?
SUBSCRIPT?
COMP-3?
COPYBOOK?
CALL?
FILE LAYOUT?
CICS?
STORAGE OVERWRITE?

Depois elimine hipóteses com evidência.

Isso é debugging profissional.

Não:

“Vou mudar isso aqui e rodar.”

🧪 O compilador é testemunha, não advogado

Ele lhe diz:

o que conseguiu compilar

Ele não garante:

o que você quis escrever

Essa distinção é central.

Um compilador pode aceitar código perfeitamente legal que implemente uma ideia completamente errada.

Exemplo:

MOVE WS-SALDO TO WS-CPF.

Talvez os campos sejam compatíveis.

O compilador não conhece sua regra de negócio.

Ele não sabe que você acabou de colocar saldo bancário dentro do CPF.

Ele apenas executa ordens.


🎯 Checklist Bellacosa para REDEFINES

Antes de aprovar qualquer REDEFINES, responda:

  • Sei exatamente qual área está sendo redefinida.

  • Conheço o tamanho da área original.

  • Conheço o tamanho de cada redefinição.

  • Entendo por que a redefinição existe.

  • Conferi COMP, COMP-3 e outros USAGE.

  • Verifiquei OCCURS.

  • Consultei o MAP.

  • Consultei referências com XREF.

  • Verifiquei copybooks relacionados.

  • Verifiquei interfaces externas.

  • Sei qual versão do compilador foi utilizada.

  • Conferi as opções efetivamente em uso.

  • Não estou confiando apenas em MAXCC=0.

Se você respondeu “não” a cinco itens:

House acabou de pedir uma ressonância.


🩺 Uma pequena autópsia de exemplo

Considere:

       01 CUSTOMER-RECORD.
          05 CUSTOMER-ID       PIC X(10).
          05 CUSTOMER-NAME     PIC X(30).

       01 CUSTOMER-ALT
          REDEFINES CUSTOMER-RECORD.
          05 CUSTOMER-DATA     PIC X(48).

O programador vê:

CUSTOMER-RECORD = 40

CUSTOMER-ALT = 48

Primeira pergunta:

Por quê?

Talvez exista uma razão.

Talvez CUSTOMER-ALT represente uma versão futura.

Talvez alguém aumentou um campo.

Talvez um copybook tenha sido alterado pela metade.

Talvez seja simplesmente erro.

O ponto é:

a diferença precisa ser explicada.


🧬 Agora imagine produção

Esse registro é enviado:

CALL 'CUSTSRV'
    USING CUSTOMER-RECORD

O programa chamado possui:

LINKAGE SECTION.

01 LK-CUSTOMER PIC X(40).

Mas o chamador manipula uma visão de 48.

Você acabou de transformar uma curiosidade de DATA DIVISION em uma investigação de interface.

Por isso mainframe não é apenas sintaxe COBOL.

É ecossistema.


🚂 O programa nunca viaja sozinho

Todo COBOL real está conectado a alguma coisa:

JCL
PROCs
Db2
CICS
IMS
VSAM
MQ
sort
files
copybooks
subprograms
APIs
RACF
z/OS

Então todo problema de layout deve ser investigado no contexto do sistema.

Esse é um dos saltos mentais mais importantes para quem começa.

Você deixa de perguntar:

“Este programa está certo?”

e começa a perguntar:

“Este programa está correto dentro dos contratos do sistema?”

Isso é maturidade.


🎬 Easter egg final: “Everybody lies”

House possui uma frase famosa:

Everybody lies.

No mainframe eu adaptaria:

Everybody assumes.

O programador assume que o campo possui 20 bytes.

O analista assume que o copybook é o mesmo.

O operador assume que MAXCC=0 significa sucesso funcional.

O arquiteto assume que a PROC é padrão.

O desenvolvedor assume que MAP estava ativo.

O time assume que aquele load module foi recompilado.

E o mainframe?

O mainframe não assume nada.

Ele executa exatamente aquilo que foi definido.


☕ Conclusão — Aprenda a investigar, não apenas a compilar

REDEFINES parece um pequeno detalhe da DATA DIVISION.

Mas ele ensina uma lição gigantesca sobre COBOL.

Ensina que existe diferença entre:

fonte

e:

layout efetivo

Entre:

intenção

e:

compilação

Entre:

programa compilado

e:

programa correto

Por isso, quando você encontrar um REDEFINES, não trate como uma palavra reservada qualquer.

Olhe o tamanho.

Olhe o mapa.

Olhe o listing.

Olhe o XREF.

Descubra quais opções de compilação estavam realmente ativas.

Saiba se vieram:

do JCL
do CBL
do PROCESS
do SYSOPTF
do option file
dos defaults da instalação

Entenda a precedência.

Conheça seu compilador.

E, principalmente, abandone uma das frases mais perigosas da profissão:

“O compilador aceitou.”

O compilador aceitar é apenas o começo da investigação.

A pergunta final continua sendo:

“O programa está fazendo exatamente aquilo que nós pensamos que ele está fazendo?”

House fecharia o prontuário.

O batch terminaria.

O café estaria frio.

E no SDSF apareceria:

MAXCC=0

Você sorriria.

Mas agora saberia que esse zero não é diagnóstico.

É apenas um sinal vital.

E programador COBOL bom não trata sinal vital.

Trata o sistema inteiro.

☕🦖

Bellacosa Mainframe — onde até um PIC X(20) pode esconder uma investigação médica, três copybooks esquecidos, duas PROCs corporativas e um comentário escrito antes de Java existir.

https://eljefemidnightlunch.blogspot.com/2022/07/alerta-vermelho-na-enterprise.html

https://eljefemidnightlunch.blogspot.com/2022/05/da-compilacao-execucao-de-um-programa.html

sexta-feira, 3 de abril de 2020

CICS BMS para Desenvolvedores COBOL – Parte 1

 

Bellacosa Mainframe mini curso de cics bms

☕ Um Café no Bellacosa Mainframe

CICS BMS para Desenvolvedores COBOL – Parte 1

Do Terminal 3270 ao Primeiro Hello World

Salve, jovem Padawan!

Se você está começando sua jornada no universo IBM Z e já ouviu alguém falar frases misteriosas como:

"Precisamos alterar o MAPSET."

"O cursor está parando no campo errado."

"O MDT não está ligado."

"Faz um SEND MAP DATAONLY."

E ficou pensando:

"Que feitiçaria mainframe é essa?"

Pode ficar tranquilo.

Neste artigo vamos iniciar uma jornada pelo BMS (Basic Mapping Support) do CICS. O objetivo é apresentar o assunto pensando em um desenvolvedor COBOL Júnior, explicando desde a origem do BMS até a construção do primeiro mapa funcionando em uma aplicação CICS.

Pegue seu café, abra seu ISPF, reserve uma aba do SDSF e venha comigo.


Um pouco de história

Voltemos algumas décadas.

Estamos nos anos 70.

Não existia navegador.

Não existia JavaScript.

Não existia React.

Não existia Angular.

Nem mesmo Windows.

O usuário corporativo acessava aplicações utilizando terminais burros conectados ao mainframe.

Entre eles destacavam-se:

  • IBM 3270

  • IBM 3278

  • IBM 3279

  • IBM 3287

A tela típica possuía:

24 linhas

80 colunas

Algo parecido com isto:

----------------------------------------
 SISTEMA DE CLIENTES

 Codigo....: _____

 Nome......: _____________________

 Cidade....: _____________________

 PF3=Sair    PF5=Atualizar

----------------------------------------

A grande questão era:

Como desenhar telas sem colocar coordenadas dentro do programa COBOL?

A IBM criou então o BMS.

Basic Mapping Support.


O nascimento do CICS

CICS significa:

Customer Information Control System

O projeto surgiu em 1968.

Originalmente desenvolvido pela IBM para atender grandes empresas que precisavam de processamento transacional online.

Enquanto o Batch processava milhares de registros durante a madrugada...

O CICS permitia:

  • consultar saldo;

  • emitir passagens;

  • cadastrar clientes;

  • aprovar empréstimos;

  • consultar apólices;

  • efetuar reservas.

Tudo em tempo real.

Hoje ainda existem milhões de transações CICS sendo executadas diariamente.


O que é BMS ?

BMS significa:

Basic Mapping Support

Ele é uma camada intermediária entre:

Programa COBOL

e

Terminal 3270.

Ele esconde detalhes do dispositivo.

O desenvolvedor não precisa conhecer:

Posição física do cursor

Bytes de atributo

Intensidade

Cor

Protocolo 3270

Ele apenas manipula variáveis COBOL.


Arquitetura do BMS

Programa COBOL
        │
        │
        ▼
+----------------+
| Symbolic Map   |
+----------------+
        │
        ▼
+----------------+
|      BMS       |
+----------------+
        │
        ▼
+----------------+
| Physical Map   |
+----------------+
        │
        ▼
Terminal 3270

Uma analogia moderna

Imagine:

HTML

CSS

JavaScript

O BMS faz papel semelhante.

Mundo WebMundo CICS
HTMLPhysical Map
JavascriptCOBOL
BrowserTerminal 3270
DOMSymbolic Map
FrameworkBMS

O que é um MAP ?

MAP é uma tela.

Exemplo:

Tela Login

Tela Consulta

Tela Menu

Tela Inclusão

Cada tela é um MAP.


Exemplo

LOGIN

Usuário:


Senha:


ENTER=Entrar
PF3=Sair

Outro MAP

MENU

1 Consultar

2 Alterar

3 Incluir


PF3 Sair

O que é um MAPSET ?

Mapset é um conjunto de mapas.

Pode possuir:

LOGIN

MENU

CONSULTA

ALTERAÇÃO

INCLUSÃO

CONFIRMAÇÃO

Tudo agrupado.

Exemplo:

SISTCLI DFHMSD

Dentro dele

LOGIN DFHMDI


MENU DFHMDI


CONSUL DFHMDI


ALTERA DFHMDI

Physical Map

É um módulo executável.

Gerado pelo assembler.

Fica em:

DFHRPL

Exemplo

CLIENTES

O CICS carrega automaticamente.


Symbolic Map

Copybook COBOL.

Vai para:

COPYLIB

SYSLIB

Exemplo

CLIENTEM

As três macros do BMS

Existem apenas três macros principais.

DFHMSD

Mapset

DFHMDI

Mapa

DFHMDF

Campo


DFHMSD

É o cabeçalho.

Exemplo

CLIENTE DFHMSD TYPE=&SYSPARM,
                 LANG=COBOL,
                 MODE=INOUT,
                 STORAGE=AUTO,
                 TIOAPFX=YES,
                 CTRL=(FREEKB)

TYPE

MAP

DSECT

FINAL


LANG

COBOL

PLI

ASM


MODE

IN

OUT

INOUT


STORAGE

AUTO

BASE=MAP-IOAREA


TIOAPFX

Recomendado:

TIOAPFX=YES

Reserva 12 bytes para BMS.


DFHMDI

Define uma tela.

Exemplo

HELLOMAP DFHMDI
         SIZE=(24,80)

DFHMDF

Define campo.

Exemplo

MSG DFHMDF POS=(5,10),
            LENGTH=20,
            ATTRB=PROT,
            INITIAL='HELLO WORLD'

Nosso primeiro Hello World

Chegou a hora.

Fonte BMS


HELLO    DFHMSD TYPE=&SYSPARM,
                LANG=COBOL,
                MODE=OUT,
                TIOAPFX=YES


TELA1    DFHMDI SIZE=(24,80)


         DFHMDF POS=(1,25),
                 LENGTH=30,
                 ATTRB=(PROT,BRT),
                 INITIAL='HELLO WORLD BMS'


         DFHMDF POS=(22,2),
                 LENGTH=40,
                 ATTRB=(ASKIP),
                 INITIAL='PF3 SAIR'


         DFHMSD TYPE=FINAL


END




O que este mapa faz?

Exibe:



            HELLO WORLD BMS




PF3 SAIR



Usuário não consegue alterar nada.

Todos campos são protegidos.


Geração do Symbolic Map

O assembler cria um copybook.

Exemplo

01 TELA1O.



05 FILLER PIC X(30).




01 TELA1I.



05 FILLER PIC X(30).


Programa COBOL

Nosso primeiro programa.


IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.


DATA DIVISION.


WORKING-STORAGE SECTION.


COPY HELLO.



PROCEDURE DIVISION.



EXEC CICS SEND

MAP('TELA1')

MAPSET('HELLO')

ERASE

FREEKB

END-EXEC.



EXEC CICS RETURN
END-EXEC.




O que acontece internamente?

Fluxo:



COBOL


SEND MAP


↓

BMS


↓

Physical Map


↓

Terminal


↓

Usuário visualiza


HELLO WORLD




A magia do BMS

Observe.

No COBOL não existe:

Linha 1

Coluna 25

Brilho

Atributo

3270

Bytes hexadecimais

Tudo está encapsulado.

O desenvolvedor apenas escreve:

SEND MAP

E o CICS faz o restante.


Curiosidade Bellacosa Mainframe

Muitos sistemas bancários escritos entre 1980 e 1995 continuam utilizando exatamente a mesma tecnologia apresentada neste artigo.

Alguns processam milhões de transações por dia.

Muitos deles executam em máquinas IBM z16 e z17, utilizando telas BMS desenvolvidas há mais de 30 anos.

É bastante comum encontrar mapas criados em 1987, recompilados diversas vezes ao longo das décadas, ainda em plena operação em seguradoras, bancos, empresas aéreas e grandes varejistas.

E isso talvez seja uma das maiores demonstrações da longevidade e da estabilidade da plataforma IBM Z.


Continua...

Na Parte 2 veremos:

✓ Compilação passo a passo do BMS

✓ JCL de Assembly

✓ Geração do Physical Map

✓ Geração do Symbolic Map

✓ Linkedit

✓ Cadastro via CEDA

✓ Uso do INSTALL

✓ Comandos CEMT

✓ SEND MAP

✓ RECEIVE MAP

✓ DFHEIBLK

✓ DFHAID

✓ Primeira pseudo-conversação CICS

terça-feira, 18 de dezembro de 2012

🔥 Como Criar uma Tela CICS BMS – Guia Definitivo Passo a Passo

 


🔥 Como Criar uma Tela CICS BMS – Guia Definitivo Passo a Passo



☕ Introdução — Antes do HTML, existia o BMS

Antes de React, Angular e CSS, o mainframe já fazia interface rica — só que com disciplina, regra e respeito ao terminal.

No mundo CICS, tela não é arte.
Tela é contrato, estado, performance e sobrevivência em produção.

Vamos do zero absoluto até a tela rodando, sem pular etapas.


🧱 Conceitos Fundamentais (Sem isso, tudo quebra)

📌 O que é BMS?

BMS (Basic Mapping Support) é a linguagem declarativa usada para definir telas CICS.

Ela descreve:

  • Campos

  • Posições

  • Atributos

  • Proteção

  • Entrada e saída de dados

📌 BMS não tem lógica.
Ele só descreve como a tela se comporta.


🧠 MAP vs MAPSET — A confusão clássica

ConceitoO que é
MAPSETConjunto de mapas (arquivo fonte BMS)
MAPUma tela individual dentro do MAPSET

📌 Analogia Bellacosa:

  • MAPSET = arquivo HTML

  • MAP = página individual

  • FIELD = input / label



🧭 Passo a Passo — Criando uma Tela CICS BMS


1️⃣ Criar o Fonte BMS (MAPSET)

Exemplo básico:

MAPSET01 DFHMSD TYPE=MAP,MODE=INOUT,LANG=COBOL,TERM=3270

Parâmetros importantes:

  • TYPE=MAP → indica que é um MAPSET

  • MODE=INOUT → entrada e saída

  • LANG=COBOL → gera copybook COBOL

  • TERM=3270 → terminal padrão

📌 Erro comum: esquecer LANG=COBOL → não gera copybook.


2️⃣ Definir um MAP (Tela)

MAP01 DFHMDI SIZE=(24,80),LINE=1,COLUMN=1

O que isso faz:

  • Cria uma tela de 24x80

  • Posiciona no canto superior

📌 Um MAPSET pode ter vários MAPs.


3️⃣ Definir Campos (DFHMDF)

Exemplo:

CAMPO1 DFHMDF POS=(5,10), LENGTH=10, ATTRB=(UNPROT,IC), INITIAL='Digite:'

Parâmetros essenciais:

ParâmetroFunção
POSLinha e coluna
LENGTHTamanho
ATTRBAtributos
INITIALTexto inicial

🎛️ Atributos Importantes (Onde mora o poder)

AtributoFunção
PROTCampo protegido
UNPROTCampo editável
ICCursor inicial
MDTCampo alterado
BRTBrilho
ASKIPPula o campo

📌 Erro clássico: esquecer MDT → CICS acha que o campo não mudou.


4️⃣ Finalizar o MAPSET

DFHMSD TYPE=FINAL END

📌 Sem isso, não compila.



⚙️ Workflow de Compilação (Onde muitos erram)

1️⃣ Fonte BMS
2️⃣ Assembler
3️⃣ Gera MAPSET (LOAD)
4️⃣ Gera COPYBOOK
5️⃣ COPYBOOK entra no programa COBOL
6️⃣ Programa SEND/RECEIVE MAP

📌 BMS não é compilado como COBOL.


🧪 Usando o MAP no Programa COBOL

Enviar a tela:

EXEC CICS SEND MAP('MAP01') MAPSET('MAPSET01') ERASE END-EXEC

Receber dados:

EXEC CICS RECEIVE MAP('MAP01') MAPSET('MAPSET01') END-EXEC

📌 O COPYBOOK gerado contém:

  • campos de entrada

  • campos de saída

  • indicadores MDT


🚀 Otimização — Dicas Bellacosa de Produção

✅ Boas práticas

  • Campos pequenos → menos tráfego

  • Use PROT para labels

  • Use UNPROT só onde necessário

  • Evite mapas gigantes

  • Reutilize MAPSETs

❌ Erros comuns

  • Um MAP por MAPSET sem necessidade

  • Tela cheia de campos UNPROT

  • Esquecer MDT

  • Misturar lógica com tela

  • Hardcode de posição no COBOL


🧠 Hierarquia Mental do BMS (Grave isso)

MAPSET ├── MAP │ ├── FIELD │ ├── FIELD │ └── FIELD

📌 Se você entende isso, não se perde mais.


🧯 Erros Clássicos em Produção

ProblemaCausa
Tela não apareceMAPSET não instalado
Campos vaziosMDT ausente
Cursor erradoIC mal posicionado
Dump AEIMMAP inexistente
Dados não chegamRECEIVE errado

🎓 Guia de Estudo Rápido

  1. Entenda MAPSET vs MAP

  2. Crie tela simples

  3. Compile e gere copybook

  4. Faça SEND

  5. Faça RECEIVE

  6. Trate MDT

  7. Otimize


💬 Comentário El Jefe Midnight Lunch

“Quem domina BMS, domina o ritmo do CICS.”


🏁 Conclusão Bellacosa

Tela CICS não é UI.
É contrato, estado e performance.

🔥 Quem respeita o BMS:

  • evita ABEND

  • entrega rápido

  • sobrevive em produção

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...