Translate

Mostrar mensagens com a etiqueta FUNCTION-ID. Mostrar todas as mensagens
Mostrar mensagens com a etiqueta FUNCTION-ID. Mostrar todas as mensagens

sexta-feira, 13 de junho de 2025

☕💣 FUNÇÕES SEM SUBPROGRAMAS? O DIA EM QUE O COBOL APRENDEU A CRIAR SUAS PRÓPRIAS APIs — E QUASE NINGUÉM PERCEBEU

 

Bellacosa Mainframe apresenta as funçoes criadas em cobol modo api on

☕💣 FUNÇÕES SEM SUBPROGRAMAS? O DIA EM QUE O COBOL APRENDEU A CRIAR SUAS PRÓPRIAS APIs — E QUASE NINGUÉM PERCEBEU

Existe um momento na carreira de todo profissional de Mainframe em que ele descobre algo e pensa:

"Como ninguém me contou isso antes?"

Foi exatamente essa sensação que muitos desenvolvedores tiveram quando conheceram as User Defined Functions (UDFs) introduzidas nas versões modernas do COBOL Enterprise da IBM.

Durante décadas, quando precisávamos reutilizar lógica em COBOL, a solução era sempre a mesma:

  • CALL de subprograma

  • COPYBOOK

  • Macro

  • Módulos compartilhados

Funcionava.

Ainda funciona.

Mas o COBOL evoluiu.

E hoje o programador pode criar suas próprias funções, utilizá-las dentro de expressões e fazer chamadas tão elegantes quanto as funções nativas do compilador.

Sim.

Da mesma forma que você usa:

FUNCTION CURRENT-DATE

ou

FUNCTION UPPER-CASE(...)

você pode criar:

FUNCTION CALCULA-IR(...)

ou

FUNCTION VALIDA-CPF(...)

ou qualquer outra regra de negócio.

Para quem passou décadas trabalhando apenas com programas e subprogramas, isso parece quase magia.

Mas não é.

É apenas COBOL moderno.


Um Pouco de História

Nas versões clássicas do COBOL:

  • COBOL VS COBOL II

  • COBOL/370

  • COBOL for MVS

não existia conceito de função definida pelo usuário.

Tudo precisava ser feito através de:

CALL "ROTINA01"

O compilador não conhecia o conceito de retorno funcional.

Quando surgiram:

  • Enterprise COBOL V5

  • Enterprise COBOL V6

a IBM passou a suportar recursos alinhados ao padrão ISO COBOL moderno.

Entre eles:

User Defined Functions

ou simplesmente:

Funções Definidas pelo Usuário


O Que é Uma User Defined Function?

É um módulo COBOL especial que:

  • recebe parâmetros

  • processa dados

  • retorna um único valor

exatamente como uma função matemática.

Exemplo:

RESULTADO =
    FUNCTION DOBRO(VALOR)

Ao invés de:

CALL "DOBRO"

Quando Vale a Pena Utilizar?

Imagine uma regra utilizada em centenas de programas.

Por exemplo:

  • cálculo de imposto

  • cálculo de juros

  • validação de CPF

  • mascaramento de dados LGPD

  • formatação de código interno

Criar uma função centralizada reduz:

  • duplicação

  • manutenção

  • erros

e aumenta a legibilidade.


Estrutura de Uma Função COBOL

O segredo está na identificação.

Observe:

IDENTIFICATION DIVISION.

FUNCTION-ID. DOBRO.

Perceba:

Não usamos:

PROGRAM-ID

Usamos:

FUNCTION-ID

Isso transforma o módulo em uma função.


Exemplo Completo

Função DOBRO

       IDENTIFICATION DIVISION.
       FUNCTION-ID. DOBRO.

       DATA DIVISION.

       LINKAGE SECTION.

       01 LK-VALOR      PIC S9(9) COMP-5.

       01 RESULTADO     PIC S9(9) COMP-5.

       PROCEDURE DIVISION
            USING LK-VALOR
            RETURNING RESULTADO.

           COMPUTE RESULTADO =
                   LK-VALOR * 2

           GOBACK.

Simples.

Recebe:

LK-VALOR

Retorna:

RESULTADO

O RETURNING

A palavra-chave fundamental é:

RETURNING

Ela define o valor devolvido pela função.

Exemplo:

PROCEDURE DIVISION
    USING ENTRADA
    RETURNING SAIDA

Sem RETURNING não existe função.


Como Chamar a Função

Agora imagine um programa principal.

IDENTIFICATION DIVISION.
PROGRAM-ID. TESTE.

Working Storage

01 WS-NUMERO       PIC 9(4).
01 WS-RESULTADO    PIC 9(5).

Chamada

MOVE 10 TO WS-NUMERO

COMPUTE WS-RESULTADO =
        FUNCTION DOBRO(WS-NUMERO)

DISPLAY WS-RESULTADO

Resultado:

20

O Que o Compilador Faz?

Quando encontra:

FUNCTION DOBRO(...)

o compilador procura um módulo com:

FUNCTION-ID. DOBRO

e gera a ligação automaticamente.

É semelhante ao que acontece com:

FUNCTION CURRENT-DATE

Em Qual Biblioteca Deve Ser Gravado?

Aqui existe uma dúvida muito comum.

A função compilada gera um módulo objeto exatamente como qualquer outro programa COBOL.

Normalmente:

OBJETO

Vai para:

&&OBJ
SYSLIN

durante a compilação.


LOAD MODULE

Após o Link Edit:

LOADLIB

ou

STEPLIB

ou

USER.LOADLIB

dependendo dos padrões da empresa.

Exemplo:

PROD.COBOL.LOAD

ou

DEV.COBOL.LOAD

Que Tipo de Objeto é Criado?

Fisicamente o compilador gera:

Object Deck

OBJETO

e depois:

Program Object

ou

Load Module

dependendo da configuração do Binder.

Ou seja:

não existe um tipo especial de dataset para funções.

A função é armazenada como um módulo executável normal.

O diferencial está no:

FUNCTION-ID

Passo a Passo Completo

Passo 1

Criar o fonte.

Exemplo:

USER.COBOL(FDOBRO)

Passo 2

Codificar:

FUNCTION-ID. DOBRO.

Passo 3

Compilar.

Exemplo de JCL:

//COBOL EXEC IGYWCL

ou

//COB EXEC PROC=IGYWCLG

Dependendo do ambiente.


Passo 4

Gerar módulo em LOADLIB.

Exemplo:

USER.LOADLIB

Passo 5

Adicionar a LOADLIB na STEPLIB.

//STEPLIB DD DSN=USER.LOADLIB,

Passo 6

Compilar os programas consumidores.

O compilador localizará a função.


Funções Com Múltiplos Parâmetros

Exemplo:

FUNCTION-ID. SOMA2.

Linkage:

01 LK-N1 PIC S9(9).
01 LK-N2 PIC S9(9).

01 RETORNO PIC S9(9).

Procedure:

PROCEDURE DIVISION
    USING LK-N1 LK-N2
    RETURNING RETORNO.

    COMPUTE RETORNO =
        LK-N1 + LK-N2

    GOBACK.

Uso:

COMPUTE TOTAL =
        FUNCTION SOMA2(10,20)

Resultado:

30

Funções Podem Chamar Outras Funções

Sim.

Exemplo:

FUNCTION DOBRO(
    FUNCTION SOMA2(5,5))

Primeiro:

SOMA2 = 10

Depois:

DOBRO = 20

Muito parecido com linguagens modernas.


Funções Podem Ser Recursivas?

Sim.

Desde que o compilador permita:

RECURSIVE

e que a lógica esteja preparada.

Mas normalmente regras de negócio não exigem isso.


Diferença Entre CALL e FUNCTION

CALL

CALL "CALCULO"

Características:

  • múltiplos parâmetros

  • sem retorno obrigatório

  • paradigma tradicional


FUNCTION

FUNCTION CALCULO(...)

Características:

  • retorno explícito

  • pode participar de expressões

  • sintaxe mais elegante


Exemplo Real de Negócio

Imagine validar CPF.

Em vez de:

CALL "CPFVAL"

você pode escrever:

IF FUNCTION CPF-VALIDO(CPF)

Muito mais legível.

A regra passa a parecer uma instrução nativa da linguagem.


Opções de Compilação Recomendadas

Nas versões atuais do Enterprise COBOL V6.x é comum utilizar:

OPT(2)
ARCH(13)
RENT
LIST
MAP
XREF
SSRANGE

Em desenvolvimento:

SSRANGE

ajuda bastante na identificação de erros.

Em produção:

NOSSRANGE

para melhor desempenho.


Compatibilidade

As User Defined Functions fazem parte das versões modernas do COBOL Enterprise.

São suportadas nas famílias atuais:

  • Enterprise COBOL V5

  • Enterprise COBOL V6

  • z/OS modernos

Sempre confirme a versão instalada junto ao time de sistemas.


Ganho Arquitetural

O maior benefício não é técnico.

É arquitetural.

Você passa a criar uma verdadeira biblioteca corporativa de regras.

Imagine uma empresa com funções:

CALCULA-IR
CALCULA-IOF
VALIDA-CPF
VALIDA-CNPJ
FORMATA-CEP

Todas reutilizadas por centenas de programas.

O resultado é:

  • menos código duplicado

  • manutenção centralizada

  • maior padronização

  • menor risco operacional


A Grande Sacada

Durante quarenta anos aprendemos que reutilização em COBOL significava:

CALL

Mas o COBOL moderno adicionou uma camada muito mais elegante.

Hoje podemos construir verdadeiras APIs corporativas diretamente dentro da linguagem usando:

FUNCTION-ID

e consumi-las com:

FUNCTION nome-da-funcao(...)

Para o desenvolvedor que ainda programa como em 1995, isso parece um detalhe.

Para quem projeta sistemas corporativos gigantescos, isso é uma mudança de paradigma.

Porque, pela primeira vez, o COBOL permite encapsular regras de negócio reutilizáveis com a mesma simplicidade com que usamos CURRENT-DATE, UPPER-CASE ou LENGTH.

E é justamente aí que mora a ironia: milhares de profissionais continuam criando subprogramas para tudo, enquanto o compilador moderno já oferece uma forma muito mais elegante de transformar regras de negócio em funções reutilizáveis.

Em outras palavras, o COBOL não virou uma linguagem nova.

Mas aprendeu um truque que muitos veteranos ainda não descobriram. ☕💣🚀


quinta-feira, 21 de julho de 2022

🚨 Alerta Vermelho na Enterprise: Descobrimos Que Não Era o COBOL 5... Era o 6.4!

Bellacosa Mainframe e atencao ao compilar uma nova função intrinseca


🚨 Alerta Vermelho na Enterprise: Descobrimos Que Não Era o COBOL 5... Era o 6.4!

Adendo — Em qual versão do Enterprise COBOL as funções definidas pelo usuário estão disponíveis?

Antes de avançarmos para a sala de máquinas, precisamos corrigir uma informação importante do artigo original:

As User-Defined Functions com FUNCTION-ID não foram introduzidas no Enterprise COBOL 5. Elas chegaram oficialmente ao IBM Enterprise COBOL for z/OS 6.4.

O suporte faz parte da implementação IBM de recursos definidos pelo padrão COBOL 2002. No Enterprise COBOL 6.4, o programador passou a poder criar funções próprias, declarar parâmetros, retornar um valor e invocá-las por meio de um identificador de função. (IBM)

🖖 Adendo do Capitão Kirk

Enterprise COBOL 6.4, compilação e JCL para User-Defined Functions

Intrínseca ou definida pelo usuário?

Existe uma diferença importante de terminologia.

Uma função como:

FUNCTION CURRENT-DATE
FUNCTION LENGTH
FUNCTION UPPER-CASE
FUNCTION NUMVAL

é uma função intrínseca, pois já vem implementada no compilador COBOL.

Uma função criada com:

FUNCTION-ID. CALCULA-DESCONTO.

é uma User-Defined Function, ou função definida pelo usuário.

Ela pode ser usada de maneira parecida com uma função intrínseca, mas não passa a fazer parte do compilador. Ela continua sendo um componente da aplicação que precisa ser compilado e disponibilizado corretamente.

Portanto, tecnicamente, não estamos adicionando uma nova função intrínseca ao compilador IBM. Estamos criando uma função de aplicação que pode ser invocada com uma sintaxe semelhante.


A partir de qual versão está disponível?

No ambiente IBM Z, o suporte a User-Defined Functions foi introduzido no:

IBM Enterprise COBOL for z/OS 6.4

O recurso utiliza o parágrafo:

FUNCTION-ID

e o delimitador:

END FUNCTION

A função também deve possuir obrigatoriamente uma cláusula RETURNING no cabeçalho da PROCEDURE DIVISION. (IBM)

Resumo de compatibilidade

Versão do Enterprise COBOLUser-Defined Function com FUNCTION-ID
Enterprise COBOL 4.xNão
Enterprise COBOL 5.1Não
Enterprise COBOL 5.2Não
Enterprise COBOL 6.1Não
Enterprise COBOL 6.2Não
Enterprise COBOL 6.3Não
Enterprise COBOL 6.4Sim

Em alguns ambientes, o Enterprise COBOL 6.4 pode precisar estar com os PTFs recomendados pela IBM instalados para que correções e recursos complementares, como protótipos de função, estejam disponíveis.

O suporte básico a User-Defined Functions pertence ao 6.4. Já o suporte ampliado a function prototypes, relacionado ao padrão COBOL 2014, foi fornecido por manutenção do compilador 6.4, incluindo o APAR/PTF associado ao suporte documentado pela IBM.


Programa completo para treinamento

Neste exemplo, criaremos a função:

CALCULA-IMPOSTO

Ela receberá:

  • valor da operação;

  • alíquota percentual;

E retornará:

  • valor do imposto.

A função e o programa principal serão colocados no mesmo membro-fonte para facilitar o laboratório.


Fonte COBOL completo

       IDENTIFICATION DIVISION.
       FUNCTION-ID. CALCULA-IMPOSTO.

       DATA DIVISION.
       LINKAGE SECTION.

       01  LK-VALOR             PIC S9(09)V99 COMP-3.
       01  LK-ALIQUOTA          PIC S9(03)V99 COMP-3.
       01  LK-IMPOSTO           PIC S9(09)V99 COMP-3.

       PROCEDURE DIVISION
           USING BY REFERENCE LK-VALOR
                              LK-ALIQUOTA
           RETURNING LK-IMPOSTO.

           COMPUTE LK-IMPOSTO ROUNDED =
               LK-VALOR * LK-ALIQUOTA / 100

           GOBACK.

       END FUNCTION CALCULA-IMPOSTO.


       IDENTIFICATION DIVISION.
       PROGRAM-ID. MAINPROG.

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.

       REPOSITORY.
           FUNCTION CALCULA-IMPOSTO.

       DATA DIVISION.
       WORKING-STORAGE SECTION.

       01  WS-VALOR             PIC S9(09)V99 COMP-3
                                VALUE 1000.
       01  WS-ALIQUOTA          PIC S9(03)V99 COMP-3
                                VALUE 18.50.
       01  WS-IMPOSTO           PIC S9(09)V99 COMP-3.

       01  WS-VALOR-EDITADO     PIC ZZZ,ZZZ,ZZ9.99-.
       01  WS-ALIQ-EDITADA      PIC ZZ9.99-.
       01  WS-IMPOSTO-EDITADO   PIC ZZZ,ZZZ,ZZ9.99-.

       PROCEDURE DIVISION.

           DISPLAY "========================================"
           DISPLAY " BELLACOSA MAINFRAME - TESTE DE FUNCAO"
           DISPLAY "========================================"

           COMPUTE WS-IMPOSTO =
               CALCULA-IMPOSTO(
                   WS-VALOR
                   WS-ALIQUOTA
               )

           MOVE WS-VALOR    TO WS-VALOR-EDITADO
           MOVE WS-ALIQUOTA TO WS-ALIQ-EDITADA
           MOVE WS-IMPOSTO  TO WS-IMPOSTO-EDITADO

           DISPLAY "VALOR DA OPERACAO : " WS-VALOR-EDITADO
           DISPLAY "ALIQUOTA           : " WS-ALIQ-EDITADA "%"
           DISPLAY "IMPOSTO CALCULADO  : " WS-IMPOSTO-EDITADO

           IF WS-IMPOSTO = 185
               DISPLAY "RESULTADO CORRETO. MISSAO CUMPRIDA."
           ELSE
               DISPLAY "RESULTADO INESPERADO. CHAME O SPOCK."
           END-IF

           GOBACK.

       END PROGRAM MAINPROG.

Por que a função aparece antes do programa?

Quando a função e o programa chamador são compilados no mesmo grupo de compilação, sem o uso de um protótipo separado, a definição da função deve aparecer antes da unidade que a utiliza.

A IBM recomenda, para projetos maiores, que a função seja mantida em arquivo separado e que um protótipo seja usado no programa chamador. Entretanto, para treinamento, colocar a função e o programa no mesmo membro é uma maneira direta de compreender o mecanismo. (IBM)

Há uma consequência importante:

A primeira unidade encontrada pelo compilador pode tornar-se o ponto de entrada padrão do módulo.

Como nossa primeira unidade é CALCULA-IMPOSTO, precisamos informar ao Binder que o verdadeiro programa inicial é:

MAINPROG

Por isso usaremos:

ENTRY MAINPROG

durante a linkedição.

Sem essa instrução, a nave pode tentar decolar pela porta da engenharia em vez de sair pela ponte de comando.


JCL completo: compilar, linkeditar e executar

O exemplo abaixo apresenta um fluxo tradicional com três etapas:

  1. COBOL — compilação;

  2. LKED — linkedição;

  3. RUN — execução.

Os nomes das bibliotecas precisam ser adaptados à instalação da sua empresa.

//BLCOBUDF JOB (ACCT),'COBOL UDF',
//             CLASS=A,
//             MSGCLASS=X,
//             MSGLEVEL=(1,1),
//             NOTIFY=&SYSUID
//*
//* ==========================================================
//* BELLACOSA MAINFRAME
//* COMPILACAO DE USER-DEFINED FUNCTION - COBOL 6.4
//* ==========================================================
//*
//COBOL    EXEC PGM=IGYCRCTL,
// PARM='LIB,OBJECT,RENT,APOST,LIST,MAP,XREF,OFFSET'
//STEPLIB  DD DISP=SHR,
//            DSN=IGY.V6R4M0.SIGYCOMP
//SYSIN    DD DISP=SHR,
//            DSN=SEU.USUARIO.COBOL(CALCUDF)
//SYSLIN   DD DSN=&&OBJETO,
//            DISP=(NEW,PASS),
//            UNIT=SYSDA,
//            SPACE=(TRK,(5,5)),
//            DCB=(RECFM=FB,LRECL=80,BLKSIZE=0)
//SYSPRINT DD SYSOUT=*
//SYSOUT   DD SYSOUT=*
//SYSUT1   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT2   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT3   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT4   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT5   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT6   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT7   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT8   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT9   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT10  DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT11  DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT12  DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT13  DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT14  DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//SYSUT15  DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//*
//LKED     EXEC PGM=IEWL,
// PARM='LIST,MAP,XREF,LET,RENT'
//SYSLIB   DD DISP=SHR,
//            DSN=CEE.SCEELKED
//         DD DISP=SHR,
//            DSN=CEE.SCEELKEX
//SYSLMOD  DD DISP=SHR,
//            DSN=SEU.USUARIO.LOAD(MAINPROG)
//SYSLIN   DD DISP=(OLD,DELETE),
//            DSN=&&OBJETO
//         DD *
  ENTRY MAINPROG
  NAME MAINPROG(R)
/*
//SYSPRINT DD SYSOUT=*
//SYSUT1   DD UNIT=SYSDA,SPACE=(CYL,(1,1))
//*
//RUN      EXEC PGM=MAINPROG,
//            COND=(0,NE)
//STEPLIB  DD DISP=SHR,
//            DSN=SEU.USUARIO.LOAD
//         DD DISP=SHR,
//            DSN=CEE.SCEERUN
//         DD DISP=SHR,
//            DSN=CEE.SCEERUN2
//SYSOUT   DD SYSOUT=*
//CEEDUMP  DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*
//

Bibliotecas que devem ser adaptadas

Biblioteca do compilador

No exemplo:

DSN=IGY.V6R4M0.SIGYCOMP

Esse nome varia de instalação para instalação.

Você poderá encontrar algo parecido com:

IGY.V6R4M0.SIGYCOMP
IGY640.SIGYCOMP
SYS1.IGY640.SIGYCOMP
CBC.SIGYCOMP

Não copie cegamente o nome do exemplo.

Pergunte ao sysprog, consulte um JCL COBOL já utilizado na empresa ou verifique a PROC oficial de compilação.


Biblioteca de runtime do Language Environment

Normalmente são utilizadas:

CEE.SCEERUN
CEE.SCEERUN2

Para a linkedição, podem aparecer:

CEE.SCEELKED
CEE.SCEELKEX

Os nomes e concatenações dependem da configuração do z/OS e do Language Environment.


Biblioteca de load modules

No exemplo:

DSN=SEU.USUARIO.LOAD

Ela precisa existir como uma PDS ou PDSE adequada para módulos executáveis.

Uma definição típica poderia ser criada por meio do ISPF 3.2 ou por JCL, usando atributos compatíveis com a instalação.

Em ambientes modernos, normalmente é preferível utilizar uma PDSE para a biblioteca de carga.


Versão mais curta usando uma PROC catalogada

Muitos ambientes IBM Z possuem procedures catalogadas como:

IGYWCL
IGYWCLG
IGYWC

Os nomes representam, em geral:

  • C: compile;

  • L: link-edit;

  • G: go ou execute.

Um exemplo conceitual seria:

//BLCOBUDF JOB (ACCT),'COBOL UDF',
//             CLASS=A,
//             MSGCLASS=X,
//             NOTIFY=&SYSUID
//*
//COBCLG   EXEC IGYWCLG,
//         PARM.COBOL='LIB,RENT,APOST,LIST,MAP,XREF'
//COBOL.SYSIN DD DISP=SHR,
//         DSN=SEU.USUARIO.COBOL(CALCUDF)
//LKED.SYSLMOD DD DISP=SHR,
//         DSN=SEU.USUARIO.LOAD(MAINPROG)
//LKED.SYSIN DD *
  ENTRY MAINPROG
  NAME MAINPROG(R)
/*
//GO.STEPLIB DD DISP=SHR,
//         DSN=SEU.USUARIO.LOAD
//GO.SYSOUT DD SYSOUT=*
//GO.CEEDUMP DD SYSOUT=*

Entretanto, há um alerta vermelho piscando na ponte:

As procedures catalogadas não são idênticas em todas as empresas.

A PROC pode:

  • possuir outro nome;

  • utilizar outros qualificadores;

  • não aceitar os mesmos overrides;

  • já incluir bibliotecas adicionais;

  • ter parâmetros obrigatórios;

  • possuir nomes internos diferentes para as etapas.

Antes de utilizá-la, execute no ISPF:

TSO ISRDDN

ou consulte os JCLs-padrão da instalação.

Também é possível procurar a PROC nas bibliotecas de procedures, normalmente concatenadas em JES2 PROCLIB.


Entendendo o REPOSITORY

O programa chamador possui:

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.

       REPOSITORY.
           FUNCTION CALCULA-IMPOSTO.

Isso informa ao compilador que CALCULA-IMPOSTO é uma função conhecida pelo programa.

Com essa declaração, podemos escrever:

COMPUTE WS-IMPOSTO =
    CALCULA-IMPOSTO(WS-VALOR WS-ALIQUOTA)

A IBM documenta que a declaração no REPOSITORY permite invocar a função sem repetir a palavra reservada FUNCTION. (IBM)

Dependendo da forma de declaração e do contexto, uma referência explícita também pode assumir a forma:

FUNCTION CALCULA-IMPOSTO(
    WS-VALOR
    WS-ALIQUOTA
)

No exemplo apresentado, utilizamos o REPOSITORY para deixar a expressão mais limpa.


Saída esperada no SYSOUT

O resultado deverá ser semelhante a:

========================================
 BELLACOSA MAINFRAME - TESTE DE FUNCAO
========================================
VALOR DA OPERACAO :       1,000.00
ALIQUOTA           :          18.50%
IMPOSTO CALCULADO  :         185.00
RESULTADO CORRETO. MISSAO CUMPRIDA.

Return codes esperados

Em uma missão bem-sucedida, procure:

COBOL RC=0000
LKED  RC=0000
RUN   RC=0000

Dependendo das opções e dos avisos encontrados, a compilação pode terminar com:

RC=0004

Isso significa que houve advertências, não necessariamente um erro fatal. Mesmo assim, analise todas as mensagens.

Interpretação tradicional

Return codeSignificado geral
0000Processamento concluído sem diagnóstico relevante
0004Advertências
0008Erros que normalmente impedem o uso seguro
0012Erros graves
0016Erro muito grave ou falha de processamento

A interpretação exata deve considerar as mensagens emitidas pelo compilador e pelo Binder.


Erro: FUNCTION-ID não reconhecido

Caso o compilador apresente mensagens indicando que:

FUNCTION-ID

não é reconhecido, as causas mais prováveis são:

  1. O JCL está chamando um compilador anterior ao Enterprise COBOL 6.4.

  2. A STEPLIB aponta para outra versão do compilador.

  3. A PROC catalogada ainda referencia COBOL 6.2 ou 6.3.

  4. O produto 6.4 está instalado, mas não é a versão selecionada pelo JCL.

  5. O fonte está sendo processado por uma ferramenta intermediária incompatível.

  6. O editor ou analisador local conhece uma gramática COBOL antiga.

Verifique no listing de compilação a identificação da versão.

Você deverá encontrar uma indicação semelhante a:

IBM Enterprise COBOL for z/OS 6.4

Não confie apenas no nome da PROC.

Uma PROC chamada COBOL64 pode ter sido alterada, enquanto uma PROC chamada apenas COBOL pode estar usando o compilador mais recente.

Como Spock diria:

“O nome de um membro não constitui evidência lógica do conteúdo de sua STEPLIB.”


Erro de linkedição: ponto de entrada incorreto

Como a função aparece primeiro no grupo de compilação, o Binder pode selecionar a entrada errada caso não seja informado explicitamente.

Por isso usamos:

ENTRY MAINPROG

Para compilações AMODE 31 com LP(32), a IBM orienta que seja fornecida uma instrução ENTRY para o programa principal quando uma compilação contém funções e programas e a função aparece antes do programa. (IBM)

Se o ENTRY estiver ausente ou incorreto, poderão ocorrer:

  • início da execução no componente errado;

  • erro de entrada não encontrada;

  • comportamento imprevisível;

  • falha de linkedição;

  • abend durante a inicialização.


Não use CALL para invocar a função

Uma User-Defined Function deve ser utilizada como função:

COMPUTE WS-IMPOSTO =
    CALCULA-IMPOSTO(WS-VALOR WS-ALIQUOTA)

Não faça:

CALL "CALCULA-IMPOSTO"

A documentação IBM alerta que invocar uma User-Defined Function por meio da instrução CALL produz comportamento imprevisível. (IBM)

Se o componente foi projetado para ser chamado com CALL, escreva-o como subprograma com:

PROGRAM-ID

Se foi projetado como função, escreva-o com:

FUNCTION-ID

Não misture as duas interfaces.


ENTRY-INTERFACE

O FUNCTION-ID também pode controlar a forma de geração e invocação por meio de ENTRY-INTERFACE.

As User-Defined Functions podem ser estruturadas para diferentes formas de ligação, incluindo:

  • estática;

  • dinâmica;

  • DLL.

O padrão documentado para ENTRY-INTERFACE é STATIC. (IBM)

Para o primeiro laboratório, mantenha a configuração padrão.

A invocação estática reduz a quantidade de peças móveis e facilita:

  • compilação;

  • linkedição;

  • testes;

  • diagnóstico;

  • implantação inicial.

Só avance para modelos dinâmicos depois de dominar:

  • protótipos;

  • external names;

  • binder;

  • load libraries;

  • Language Environment;

  • convenções de interface.


Cuidado com BY REFERENCE

No exemplo, os parâmetros são recebidos com:

USING BY REFERENCE

Isso significa que a função recebe referências aos itens do programa chamador.

Embora uma boa função de cálculo não deva modificar seus argumentos, o uso de referência exige disciplina.

Evite comandos como:

MOVE ZERO TO LK-VALOR

Isso poderia alterar dados pertencentes ao chamador, dependendo da interface e do argumento utilizado.

Para obter um comportamento semelhante ao BY CONTENT, o Enterprise COBOL 6.4 disponibiliza a função CONTENT-OF. Nesse modelo, a definição formal continua usando BY REFERENCE, mas o argumento é protegido por uma cópia temporária criada na invocação. (IBM)

Exemplo conceitual:

REPOSITORY.
    FUNCTION CONTENT-OF INTRINSIC
    FUNCTION CALCULA-IMPOSTO.

Invocação:

COMPUTE WS-IMPOSTO =
    CALCULA-IMPOSTO(
        CONTENT-OF(WS-VALOR)
        CONTENT-OF(WS-ALIQUOTA)
    )

Isso é especialmente útil quando você deseja deixar claro que a função não deve alterar os argumentos fornecidos.


Dicas de compilação do Scotty

1. Comece sem otimização agressiva

Durante os primeiros testes, prefira opções que facilitem diagnóstico.

Por exemplo:

LIST,MAP,XREF,OFFSET

Depois que o programa estiver estabilizado, avalie as opções de otimização adotadas pela instalação.


2. Use TEST em ambiente de desenvolvimento

Caso a empresa utilize IBM Debug for z/OS, as opções apropriadas de depuração podem ajudar a acompanhar:

  • entrada na função;

  • conteúdo dos parâmetros;

  • valor retornado;

  • fluxo de execução.

As opções exatas dependem das ferramentas e padrões locais.


3. Verifique o listing

No listing de compilação, confirme:

  • versão do compilador;

  • opções efetivamente utilizadas;

  • definição da função;

  • referências à função;

  • mensagens de severidade;

  • offsets;

  • informações do objeto gerado.


4. Não esconda erros com COND inadequado

No laboratório, a execução deve ocorrer apenas quando as etapas anteriores forem bem-sucedidas.

Uma alternativa moderna é usar:

//RUN EXEC PGM=MAINPROG,
// IF (COBOL.RC LE 4 AND LKED.RC LE 4) THEN

ou estruturas IF/THEN/ELSE/ENDIF do JCL, conforme o padrão da empresa.

Evite executar um módulo quando a compilação terminou com erro grave.


5. Teste limites numéricos

Não teste apenas:

1000 × 18,5%

Teste também:

  • valor zero;

  • alíquota zero;

  • valor negativo, se permitido;

  • alíquota com muitas casas;

  • valor máximo do PIC;

  • resultado próximo de overflow;

  • arredondamento;

  • sinal;

  • casas decimais;

  • truncamento.


Checklist de lançamento da função

Antes de Kirk autorizar a partida, confirme:

[ ] Enterprise COBOL 6.4 está realmente sendo utilizado
[ ] FUNCTION-ID foi reconhecido
[ ] A função possui RETURNING
[ ] A função termina com END FUNCTION
[ ] O programa termina com END PROGRAM
[ ] A função aparece antes do chamador no mesmo grupo
[ ] O REPOSITORY declara a função
[ ] ENTRY MAINPROG foi fornecido ao Binder
[ ] A load library existe
[ ] CEE.SCEERUN está disponível na execução
[ ] Os parâmetros possuem formatos compatíveis
[ ] O valor de retorno cabe no campo receptor
[ ] Não existe CALL para a User-Defined Function
[ ] O listing foi revisado
[ ] Os testes de limite foram executados

Diário de bordo do Capitão Kirk

Criar uma função definida pelo usuário no Enterprise COBOL 6.4 não é apenas aprender uma nova sintaxe.

É aprender que o COBOL também evolui.

A linguagem que processava cartões perfurados agora pode organizar regras em funções reutilizáveis, oferecer interfaces mais expressivas e aproximar sistemas críticos de práticas modernas de engenharia de software.

Mas o Padawan precisa compreender toda a cadeia:

FONTE
  ↓
COMPILADOR COBOL 6.4
  ↓
OBJETO
  ↓
BINDER
  ↓
LOAD MODULE
  ↓
LANGUAGE ENVIRONMENT
  ↓
EXECUÇÃO

A função não “se instala” como um plugin.

Ela é:

  1. escrita;

  2. compilada;

  3. linkedita;

  4. armazenada em uma load library;

  5. disponibilizada ao programa que a utilizará.

Na ponte da Enterprise, Kirk conclui:

“Não basta descobrir um novo recurso. É preciso saber como colocá-lo em produção sem explodir os motores.”

Spock examina o listing e responde:

“Compilação RC zero, linkedição RC zero e resultado igual a 185. A missão é logicamente satisfatória.”

E Scotty, olhando para o Binder, acrescenta:

“Desde que ninguém esqueça o ENTRY MAINPROG, capitão.”

Esse pequeno detalhe resume uma grande verdade do mundo mainframe:

O código pode estar perfeito, mas a missão só termina quando compilação, linkedição e execução trabalham como uma única tripulação.

 

quarta-feira, 20 de julho de 2022

Criando Sua Própria Função Intrínseca no COBOL

 

Bellacosa Mainframe crie sua propria função intrinseca no Cobol Mainframe

☕ Um Café no Bellacosa Mainframe

Criando Sua Própria Função Intrínseca no COBOL

O Guia do Programador COBOL Padawan para Expandir os Poderes da Linguagem com a Ajuda do Capitão Kirk

"O universo não se expande porque alguém pediu permissão. Ele se expande porque alguém teve coragem de explorar o desconhecido."

— Capitão James T. Kirk (adaptado para Bellacosa Mainframe)


Introdução — "Mas... eu posso criar uma nova FUNCTION no COBOL?"

Essa é uma das perguntas que quase nenhum curso responde.

Durante décadas, programadores COBOL aprenderam algo parecido com isto:

  • FUNCTION CURRENT-DATE

  • FUNCTION UPPER-CASE

  • FUNCTION LOWER-CASE

  • FUNCTION LENGTH

  • FUNCTION RANDOM

E a conclusão natural era:

"Essas funções fazem parte da linguagem. Não posso criar outras."

Durante muitos anos isso realmente era verdade.

Mas a IBM resolveu mudar isso.

Hoje, no Enterprise COBOL moderno, você pode criar suas próprias funções, reutilizáveis exatamente como uma FUNCTION nativa da linguagem.

É quase como ensinar novos truques ao compilador.

Se você já trabalhou com:

  • Java (methods)

  • C (functions)

  • Python (def)

  • C# (methods)

vai perceber que finalmente o COBOL ganhou algo semelhante.

E a melhor parte?

Isso continua extremamente eficiente para ambientes bancários.

Hoje vamos descobrir como.

E, como sempre, o Capitão Kirk nos acompanhará nessa missão.


A ponte da Enterprise

Imagine a USS Enterprise.

Cada oficial possui uma especialidade.

Spock calcula.

Scotty cuida do motor.

McCoy resolve problemas médicos.

Kirk apenas chama quem precisa.

Ele não faz tudo.

Ele reutiliza especialistas.

Uma User-Defined Function faz exatamente isso.

Você cria um especialista.

Depois qualquer programa COBOL pode chamá-lo.


Antes disso...

Durante mais de 50 anos havia apenas dois caminhos.

Opção 1

Escrever código repetido.

CALCULA-JUROS.

CALCULA-DESCONTO.

CALCULA-IDADE.

Copiado dezenas de vezes.


Opção 2

Criar um subprograma

CALL "JUROS"

Funciona.

Mas possui custo.

Existe:

  • parameter list

  • linkage

  • transferência de controle

  • convenções de chamada

Embora eficiente, ainda existe overhead.


As User Defined Functions diminuem bastante esse problema.


O que é uma User-Defined Function?

É uma função escrita pelo próprio programador.

Ela recebe parâmetros.

Executa lógica.

Retorna um único valor.

Exemplo imaginário:

FUNCTION CALCULA-CPF()

ou

FUNCTION REMOVE-ESPACOS()

ou

FUNCTION SOMA-IMPOSTOS()

Ela parece uma função da linguagem.

Mas foi você quem escreveu.


Desde quando isso existe?

No mundo IBM Mainframe, o suporte chegou com o IBM Enterprise COBOL 5 (introduzido em 2013), quando a linguagem passou a incorporar recursos modernos alinhados aos padrões mais recentes do COBOL. As capacidades foram ampliadas e refinadas nas versões 6.1, 6.2, 6.3, 6.4 e 6.5, que hoje são as mais encontradas em ambientes z/OS corporativos.

Na prática, é nas versões Enterprise COBOL 5.x e, principalmente, 6.x que esse recurso se tornou realmente utilizável em projetos modernos.


Precisa instalar alguma coisa?

Boa notícia.

Não.

Nada.

Se o compilador suporta User-Defined Functions, elas já fazem parte do compilador.

Você apenas compila normalmente.

Não existe:

  • DLL

  • Plugin

  • Biblioteca externa


Como funciona?

A arquitetura é simples.

Programa A

↓

FUNCTION MINHA-FUNCTION()

↓

User Defined Function

↓

Retorna resultado

Muito parecido com isto:

resultado =
FUNCTION JUROS(valor)


Estrutura geral

Ela lembra um programa COBOL.

IDENTIFICATION DIVISION

FUNCTION-ID.

ENVIRONMENT DIVISION.

DATA DIVISION.

PROCEDURE DIVISION.

END FUNCTION.

Observe algo curioso.

Não usamos

PROGRAM-ID

Usamos

FUNCTION-ID

Esse é o detalhe que transforma um programa em uma função.


Exemplo 1

Dobrando um número

IDENTIFICATION DIVISION.
FUNCTION-ID. DOBRO.

DATA DIVISION.

LINKAGE SECTION.

01 L-VALOR PIC S9(9) COMP-5.

01 L-RETORNO PIC S9(9) COMP-5.

PROCEDURE DIVISION USING L-VALOR
                   RETURNING L-RETORNO.

    COMPUTE L-RETORNO = L-VALOR * 2

    GOBACK.

END FUNCTION DOBRO.

Bonito.

Limpo.

Pequeno.


Agora o programa principal.

DISPLAY FUNCTION DOBRO(15)

Saída

30

Muito elegante.


Exemplo 2

Maior entre dois números

FUNCTION MAIOR(A,B)

Dentro:

IF A > B
   MOVE A TO RETORNO
ELSE
   MOVE B TO RETORNO
END-IF

Depois:

DISPLAY FUNCTION MAIOR(25 19)

Resultado

25


Exemplo 3

Calcular idade

FUNCTION IDADE(DATA-NASCIMENTO)

Internamente:

CURRENT-DATE

Subtrai

Retorna idade.

Depois:

DISPLAY FUNCTION IDADE(19980412)

Muito mais legível.


Onde declarar parâmetros?

Na

LINKAGE SECTION

Exemplo

01 PARAM1.

01 PARAM2.

Depois

PROCEDURE DIVISION USING
PARAM1
PARAM2
RETURNING RESULTADO

Muito parecido com um subprograma.


Como chamar?

COMPUTE WS-TOTAL =
FUNCTION JUROS(1000 15)

Ou

MOVE FUNCTION DOBRO(5)
TO WS-VALOR

Ou

DISPLAY FUNCTION TEXTO()


Posso chamar outras FUNCTIONS?

Sim.

Inclusive funções intrínsecas.

FUNCTION UPPER-CASE()

Dentro da sua função.

Exemplo

FUNCTION UPPER-CASE(cliente)

Retorna

JOÃO


Posso chamar um CALL?

Também.

CALL "ROTINA"

Nada impede.

Mas cuidado.

Funções devem ser pequenas.


O Capitão Kirk explica

Imagine que Kirk pede um café.

Ele não desmonta o replicador.

Ele apenas diz:

CAFÉ()

O replicador faz todo o trabalho.

Isso é encapsulamento.


Performance

Excelente.

Muito melhor do que copiar código.

Muito melhor para manutenção.

Além disso:

✔ reutilização

✔ cache de instruções

✔ otimização do compilador

✔ menor duplicação


Um detalhe importante

Funções devem retornar UM único valor.

Não dez.

Se precisa retornar muitas estruturas:

Use

CALL

Não FUNCTION.


Posso acessar arquivos VSAM?

Tecnicamente pode.

Mas...

Não deveria.

Funções devem ser previsíveis.


Boa função:

CALCULA

Ruim:

ABRE VSAM

FAZ I/O

ATUALIZA DB2

ENVIA MQ


O perigo dos efeitos colaterais

Imagine:

FUNCTION SALDO()

Você espera apenas consultar.

Mas internamente:

UPDATE DB2

Isso é perigoso.

Uma função deve produzir sempre o mesmo resultado para a mesma entrada, sempre que possível.


Erros comuns

1 Repetir lógica

Ao invés de:

FUNCTION IMPOSTO()

o programador copia:

COMPUTE...

100 vezes.


2 Fazer função enorme

Ruim

900 linhas

Boa

20

40

60 linhas


3 Muitos parâmetros

FUNCTION CALCULA(
A
B
C
D
E
F
G
H)

Já virou um monstro.


4 Fazer I/O

Evite.


5 Alterar estado global

Funções devem ser independentes.


Truques interessantes

Criar biblioteca matemática

FUNCTION MEDIA()

FUNCTION DESVIO()

FUNCTION MEDIANA()


Biblioteca financeira

JUROS

IOF

IR

TAXA


Biblioteca bancária

VALIDA-CPF

VALIDA-CNPJ

MOD11

DIGITO


Biblioteca texto

REMOVE-ESPACOS

NORMALIZA

CAPITALIZA


Organização recomendada

COPYBOOKS

FUNCTIONS

SUBPROGRAMAS

PROGRAMAS

Cada função em seu próprio membro.

Muito semelhante à organização de uma biblioteca reutilizável.


Boas práticas

✔ Nomeie funções de forma descritiva (CALCULA-JUROS, VALIDA-CPF, NORMALIZA-NOME).

✔ Mantenha apenas uma responsabilidade por função.

✔ Evite acessar arquivos, filas MQ ou banco de dados dentro de funções de cálculo.

✔ Prefira funções determinísticas: mesma entrada, mesma saída.

✔ Documente parâmetros e tipo de retorno.

✔ Teste cada função de forma isolada antes de utilizá-la em produção.

✔ Centralize funções reutilizáveis em bibliotecas compartilhadas pela equipe.

✔ Utilize compilação otimizada nas versões atuais do Enterprise COBOL para aproveitar as melhorias do compilador.


Quando usar FUNCTION e quando usar CALL?

SituaçãoFUNCTIONCALL
Cálculos rápidos✅ Excelente✔ Possível
Manipulação de texto✅ Excelente✔ Possível
Validação de CPF/CNPJ✅ Ideal✔ Possível
Retornar um único valor✅ Melhor escolha✔ Possível
Atualizar Db2❌ Evite✅ Recomendado
Processar VSAM❌ Evite✅ Recomendado
Enviar mensagens MQ❌ Evite✅ Recomendado
Processamento complexo com vários retornos❌ Não indicado✅ Ideal

Um exemplo prático: função para calcular desconto

Imagine uma empresa que aplica um desconto de 10% sobre qualquer valor informado. Em vez de repetir o cálculo em dezenas de programas, criamos uma função reutilizável.

Função

IDENTIFICATION DIVISION.
FUNCTION-ID. CALC-DESCONTO.

DATA DIVISION.

LINKAGE SECTION.
01 LK-VALOR     PIC S9(7)V99 COMP-3.
01 LK-RETORNO   PIC S9(7)V99 COMP-3.

PROCEDURE DIVISION
    USING LK-VALOR
    RETURNING LK-RETORNO.

    COMPUTE LK-RETORNO =
        LK-VALOR * 0.90

    GOBACK.

END FUNCTION CALC-DESCONTO.

Programa chamador

IDENTIFICATION DIVISION.
PROGRAM-ID. TESTE.

DATA DIVISION.
WORKING-STORAGE SECTION.

01 WS-VALOR     PIC S9(7)V99 VALUE 1000.
01 WS-FINAL     PIC S9(7)V99.

PROCEDURE DIVISION.

    MOVE FUNCTION CALC-DESCONTO(WS-VALOR)
        TO WS-FINAL

    DISPLAY "Valor original : " WS-VALOR
    DISPLAY "Valor final    : " WS-FINAL

    STOP RUN.

Saída esperada:

Valor original : 1000.00
Valor final    :  900.00

Perceba como o programa principal permanece limpo. Toda a regra de negócio fica encapsulada em um único lugar. Se amanhã o desconto passar para 12%, basta recompilar a função.


A visão do Capitão Kirk

No final da missão, Kirk reúne sua tripulação na ponte da Enterprise.

Spock observa:

"Capitão, seria lógico repetir o mesmo algoritmo em 147 programas?"

Kirk sorri.

"Claro que não, Spock. Criamos uma função, compartilhamos conhecimento e seguimos explorando."

Scotty complementa:

"Quanto menos motores improvisados, menos explosões na sala de máquinas."

McCoy, olhando para um enorme programa COBOL cheio de código duplicado, comenta:

"Ele está vivo... mas não por muito tempo."

Essa é a verdadeira filosofia das User-Defined Functions: transformar conhecimento reutilizável em componentes pequenos, claros e confiáveis.

Cuidados e atenção 

Como compilar e ajustar sua função intrinseca no Cobol 6.4

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

O que são Funções Intrincsecas no Cobol

https://eljefemidnightlunch.blogspot.com/2022/09/funcoes-intrinsecas-no-enterprise-cobol.html



Conclusão — Seu próximo passo como Padawan COBOL

As User-Defined Functions representam uma das evoluções mais elegantes do COBOL moderno. Elas aproximam a linguagem das práticas de engenharia de software utilizadas em linguagens contemporâneas, sem abrir mão da robustez que tornou o COBOL o alicerce de bancos, seguradoras e grandes empresas por décadas.

Para um Padawan COBOL, dominar esse recurso significa deixar de escrever programas monolíticos e começar a construir bibliotecas reutilizáveis de regras de negócio. Isso reduz duplicação de código, facilita testes, melhora a manutenção e torna o desenvolvimento muito mais organizado.

No universo Bellacosa Mainframe, criar uma nova função é como adicionar um novo oficial especializado à tripulação da USS Enterprise. Cada função tem uma missão específica, executa seu trabalho com excelência e permite que o programa principal permaneça simples, elegante e focado em coordenar a missão.

Porque, no fim das contas, os melhores sistemas corporativos não são aqueles que possuem mais linhas de código, mas aqueles em que cada componente conhece exatamente seu papel — assim como cada oficial da Frota Estelar conhece sua estação na ponte de comando.