⬅️ Volver a la clase 146 · 🧬 Primos del Atlas · 🧟 Índice de lenguajes vivos · 📚 Índice
Un identificador es válido si está todo en minúsculas. Es una regla de estilo arbitraria, y ahí está el punto de esta clase: casi todas lo son. Lo que no es arbitrario es quién la comprueba. Y esta página tiene los dos extremos del mundo: MISRA y las normas de certificación de Ada, donde cada desviación se documenta y se justifica ante un auditor, y M, donde la convención más importante nació de un límite físico: los nombres no podían pasar de ocho caracteres.
🎯 Estos lenguajes no están aquí por ser antiguos
El criterio es doble y se declara en la ficha de cada uno: que se ejecute hoy —banca, sanidad, aviónica, ERP, diseño de chips, CAD— y que deje a la vista un concepto que los diez del núcleo esconden.
Aquí el concepto es el estándar de codificación y la revisión como proceso, y estos lenguajes lo enseñan porque inventaron la disciplina. La revisión formal de código —con roles, actas y métricas— se formalizó en IBM en 1976, sobre programas COBOL y PL/I. Los estándares de codificación restrictivos —MISRA, JSF, SPARK— nacieron en C y Ada para sistemas donde un fallo mata. Y Perl aportó la idea opuesta: un analizador que aplica un libro de estilo, configurable y con severidades.
Y aparece la pregunta que decide el valor de una revisión: ¿qué debe mirar una persona, y qué debe mirar una máquina?
Y ninguno es una foto fija: casi todos han incorporado en los últimos años JSON, REST, GPU, Unicode o Git. Cada ficha lo detalla en su sección
🔄 Lo que se ha modernizado.
valido=<true|false> (true si está todo en minúsculas)valido si todos los caracteres son minúsculas| stdin | esperado |
|---|---|
total |
valido=true |
Total |
valido=false |
abc |
valido=true |
Qué está verificado en esta página. Los lenguajes de la sección 🟢 se ejecutan en CI contra este mismo
casos.json, igual que las diez implementaciones del núcleo (workflow Labs). Los de la sección 🟡 no pueden cumplir este contrato sin falsear el lenguaje, y se explica por qué. Los de la sección ⚪ sí podrían, pero su cadena de herramientas no está en los runners: son correctos, sin sello de máquina.
Ficha completa · Banca, seguros, gobierno, medios de pago · cobc -x -free prog.cob
IDENTIFICATION DIVISION.
PROGRAM-ID. ESTILO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LINEA PIC X(60).
01 I PIC 9(4) COMP.
01 LG PIC 9(4) COMP.
01 VALIDO PIC X(5) VALUE "true".
PROCEDURE DIVISION.
ACCEPT LINEA
MOVE FUNCTION LENGTH(FUNCTION TRIM(LINEA)) TO LG
PERFORM VARYING I FROM 1 BY 1 UNTIL I > LG
IF LINEA(I:1) IS NOT ALPHABETIC-LOWER
MOVE "false" TO VALIDO
END-IF
END-PERFORM
DISPLAY "valido=" FUNCTION TRIM(VALIDO)
STOP RUN.
Lo que esta clase enseña en COBOL. El programa usa ALPHABETIC-LOWER, una condición de clase
integrada en el lenguaje: COBOL tiene NUMERIC, ALPHABETIC, ALPHABETIC-UPPER y ALPHABETIC-LOWER
como comprobaciones nativas, más las clases definidas por el usuario con CLASS.
Y sobre revisión, aquí está el origen histórico que el "por qué" de esta clase anunciaba: la inspección de código formal, inventada por Michael Fagan en IBM en 1976.
Y merece detallarse porque casi todo lo que hoy se hace en una revisión viene de ahí:
Los roles están separados:
Las reglas duras:
Los datos de IBM fueron contundentes: las inspecciones encontraban entre el 60 % y el 90 % de los defectos, y detectarlos ahí costaba entre 10 y 100 veces menos que en producción.
Y el dato del ritmo sigue siendo la crítica más útil a la revisión moderna: una petición de cambios de 800 líneas revisada en veinte minutos no es una revisión. Los estudios de Cisco y SmartBear de los años 2000 confirmaron el mismo límite, con el mismo número.
Y las herramientas actuales del mundo COBOL:
| Herramienta | Qué comprueba |
|---|---|
| SonarQube (plugin COBOL) | complejidad, duplicación, reglas de mantenibilidad |
cobolint / GnuCOBOL -Wall |
avisos del compilador como norma |
| CAST / Micro Focus Enterprise Analyzer | análisis de sistemas enteros y grafo de impacto |
GO TO prohibido salvo GO TO ... EXIT |
la regla de estilo más extendida en COBOL |
La última merece explicarse: COBOL permite ALTER y saltos arbitrarios, y el estándar de facto de
la industria desde los años ochenta es prohibirlos — dejando solo GO TO hacia la etiqueta de
salida de un párrafo.
Es la aplicación práctica de la programación estructurada, impuesta por norma de equipo, en un lenguaje que nunca la impuso.
Ficha completa · HPC, clima, física, BLAS/LAPACK · gfortran -O2 prog.f90
program estilo
implicit none
character(len=60) :: linea
integer :: i, n, c
logical :: valido
read(*, '(A)') linea
n = len_trim(linea)
valido = n > 0
do i = 1, n
c = iachar(linea(i:i))
if (c < iachar('a') .or. c > iachar('z')) valido = .false.
end do
if (valido) then
write(*, '(A)') 'valido=true'
else
write(*, '(A)') 'valido=false'
end if
end program estilo
Lo que esta clase enseña en Fortran. El programa compara con iachar, que da el código ASCII
del carácter — frente a ichar, que da el del juego de caracteres del procesador. En una máquina
EBCDIC eso importa, y usar iachar hace el programa portable.
Y sobre estándares, Fortran tiene el suyo escrito en la primera línea de todos los programas de este
curso: implicit none.
La clase 137 contó por qué. Aquí importa la otra mitad: es una regla de estilo que el compilador puede imponer.
gfortran -fimplicit-none -Wall -Wextra -std=f2018 -pedantic
-std=f2018 -pedantic rechaza las extensiones no estándar, que es la regla más valiosa en un
lenguaje con sesenta años de extensiones específicas de cada fabricante.
Y las reglas que la comunidad científica ha consolidado, y que merecen conocerse porque son sustanciales:
| Regla | Motivo |
|---|---|
implicit none siempre |
los nombres mal escritos crean variables (clase 137) |
Todo en module |
activa la comprobación de interfaces (clase 109) |
intent(in/out/inout) en cada argumento |
documenta y hace comprobar la dirección |
private por defecto en los módulos |
public :: solo_lo_que_exporto |
Nada de common, equivalence ni goto calculado |
de la era de las tarjetas |
real(dp) con dp de iso_fortran_env |
y nunca real*8, que no es estándar |
pure y elemental donde se pueda |
permite optimizar y documenta que no hay efectos |
intent merece el detalle, porque es una regla de estilo con consecuencias reales: declarar
intent(in) hace que el compilador rechace cualquier modificación del argumento, y permite pasar
por referencia sin copia con seguridad.
Es documentación que el compilador comprueba, que es lo mejor que puede ser una convención.
Y las herramientas:
fprettify --indent 3 --strict-indent # formateo determinista
fortran-linter prog.f90
findent -ofree # convertir formato fijo a libre
Y merece cerrar con la observación cultural que esta clase permite: el código científico se revisa poco, porque históricamente lo escribía una persona sola, para su propia tesis, y nadie más lo leía.
La consecuencia se ve en el legado: programas de cien mil líneas sin pruebas, sin módulos y con variables de tres letras. Y la respuesta de la comunidad en la última década —revistas que exigen el código, revisión de software científico, iniciativas como el Journal of Open Source Software— es exactamente esta clase aplicada a un campo que la necesitaba.
Ficha completa · Aviónica, espacio, ferrocarril, defensa · gnatmake prog.adb
with Ada.Text_IO; use Ada.Text_IO;
procedure Estilo is
Linea : String (1 .. 60);
Ultimo : Natural;
Valido : Boolean;
begin
Get_Line (Linea, Ultimo);
Valido := Ultimo > 0;
for I in 1 .. Ultimo loop
if Linea (I) not in 'a' .. 'z' then
Valido := False;
end if;
end loop;
if Valido then
Put_Line ("valido=true");
else
Put_Line ("valido=false");
end if;
end Estilo;
Lo que esta clase enseña en Ada. Linea (I) not in 'a' .. 'z' usa la pertenencia a un rango de un
tipo enumerado —Character lo es— que es una construcción del lenguaje, no una comparación numérica.
Y sobre estándares, Ada tiene los más estrictos de esta página, y merece ver cómo funcionan de verdad.
Primero, el lenguaje impone restricciones por declaración:
pragma Restrictions (No_Allocators); -- prohibido reservar en el montón
pragma Restrictions (No_Recursion); -- prohibida la recursión
pragma Restrictions (No_Secondary_Stack); -- sin pila secundaria
pragma Restrictions (No_Exception_Propagation); -- las excepciones no suben
pragma Restrictions (Max_Tasks => 8); -- como mucho ocho tareas
pragma Profile (Ravenscar); -- el perfil de tiempo real
Esas restricciones las comprueba el compilador y se niega a compilar si se violan. No son recomendaciones: son parte del programa.
Y el perfil Ravenscar es un conjunto de restricciones sobre la concurrencia (clase 135) que hace el sistema analizable: sin creación dinámica de tareas, sin entradas con guardas complejas, con prioridades fijas — de modo que se puede demostrar que se cumplen los plazos.
Es una decisión de ingeniería que merece entenderse: se renuncia deliberadamente a la mitad del lenguaje para poder demostrar propiedades. Y funciona: Ravenscar se usa en satélites y en control de vuelo.
Segundo, las normas del sector:
| Norma | Ámbito |
|---|---|
| DO-178C | aviónica; niveles A a E, con MC/DC en el nivel A |
| EN 50128 | ferrocarril |
| IEC 61508 | seguridad funcional industrial |
| JSF++ / MISRA | los equivalentes en C++ y C |
| AdaCore Coding Standard | el estándar de estilo con gnatcheck |
Y la propiedad que las define todas: cada desviación se documenta, se justifica técnicamente y la aprueba un auditor. No hay "lo dejo así porque tengo prisa".
Y tercero, las herramientas:
gnatcheck -rules -from=proyecto.rules # reglas de codificación
gnatmetric # complejidad ciclomática, anidamiento
gnatpp # formateo determinista
gnatprove # DEMOSTRACIÓN de ausencia de errores
Y merece cerrar con lo que esto significa para la revisión humana, porque es la respuesta a la pregunta del "por qué" de esta clase: cuando las reglas mecánicas las aplica el compilador y las propiedades las demuestra una herramienta, la revisión humana se dedica íntegramente a si el requisito es el correcto.
Y en estos sistemas ese es, de hecho, el sitio donde están los fallos que matan: no en el código, en la especificación.
Ficha completa · Escritorio empresarial, TPV, industria (vía Delphi y Free Pascal) · fpc -Mobjfpc prog.pas
program Estilo;
{$MODE OBJFPC}{$H+}
uses SysUtils;
var
Linea: string;
I: Integer;
Valido: Boolean;
begin
ReadLn(Linea);
Linea := Trim(Linea);
Valido := Length(Linea) > 0;
for I := 1 to Length(Linea) do
if not (Linea[I] in ['a'..'z']) then
Valido := False;
if Valido then
WriteLn('valido=true')
else
WriteLn('valido=false');
end.
Lo que esta clase enseña en Pascal. Linea[I] in ['a'..'z'] usa el operador de conjuntos de
Pascal (clase 094): ['a'..'z'] es un conjunto literal, y in comprueba la pertenencia en una sola
instrucción de máquina, con máscaras de bits.
Es una construcción de 1970 que sigue siendo más legible que la comparación doble de casi todos los lenguajes de esta página.
Y sobre estilo, Pascal tiene una historia particular: fue diseñado como lenguaje de enseñanza, y eso le dio una cultura de estilo explícita desde el principio.
Las convenciones del ecosistema, muy estables desde los años noventa:
| Convención | Ejemplo |
|---|---|
Tipos con T |
TCliente, TFormaPago |
Interfaces con I |
IRepositorio |
Campos privados con F |
FNombre |
Argumentos con A |
ANombre |
| PascalCase para todo lo público | CalcularTotal |
| Constantes con prefijo del grupo | clRojo, mrOk |
El prefijo F para campos merece la explicación, porque no es capricho: en Delphi, una propiedad
y su campo de respaldo tienen el mismo nombre conceptual:
private
FNombre: string;
published
property Nombre: string read FNombre write SetNombre;
Sin el prefijo, habría colisión, así que la convención resuelve una restricción real del lenguaje. Y esa es la clase de regla de estilo que sobrevive: la que resuelve algo.
Y las herramientas del ecosistema:
ptop -c ptop.cfg entrada.pas salida.pas # el formateador de Free Pascal
| Herramienta | Qué hace |
|---|---|
ptop |
formateador incluido en Free Pascal |
| Jedi Code Format | el formateador de referencia en Delphi |
| Pascal Analyzer (Peganza) | análisis estático profundo: variables no usadas, ámbitos |
{$WARN ... ERROR} |
convertir avisos concretos en errores |
{$WARN SYMBOL_DEPRECATED ERROR} merece la mención, porque es la forma limpia de aplicar una regla
de equipo: marcar lo obsoleto y hacer que su uso no compile.
procedure MetodoViejo; deprecated 'usa MetodoNuevo';
Es documentación, aviso y regla de estilo en una declaración, y el compilador la hace cumplir — que es lo que el cierre de esta clase pide.
Ficha completa · IA simbólica, CAD, investigación · sbcl --script prog.lisp
(let ((palabra (string-trim '(#\Space #\Return) (read-line))))
(format t "valido=~A~%"
(if (and (plusp (length palabra))
(every #'lower-case-p palabra))
"true" "false")))
Lo que esta clase enseña en Common Lisp. every con #'lower-case-p es la forma idiomática de
comprobar una propiedad sobre toda una secuencia (clase 115), y plusp comprueba que sea positiva —Lisp
tiene predicados con nombre para casi todo.
Y sobre estilo, Lisp tiene convenciones muy consolidadas y muy informativas, porque el nombre dice el tipo de cosa:
| Convención | Significado |
|---|---|
nombre-p / nombrep |
predicado: devuelve verdadero o falso |
*variable-global* |
earmuffs: variable especial (clase 088) |
+constante+ |
constante definida con defconstant |
%interno% o %funcion |
de bajo nivel, no usar desde fuera |
nombre! (raro en CL) |
destructivo — en Scheme es la norma |
n de prefijo: nreverse |
destructivo: reutiliza la estructura |
with-... |
macro que establece un contexto y lo deshace |
do-... |
macro de iteración |
define-... / def... |
definición |
Los asteriscos de *variable* merecen destacarse porque no son decorativos: marcan que la variable
tiene alcance dinámico (clase 088), y enlazarla con let afecta a todo lo que se llame desde
ahí.
Confundir una variable especial con una léxica produce fallos muy difíciles de encontrar, y la convención de nombres es la única defensa, porque el lenguaje no lo distingue sintácticamente.
Es un ejemplo perfecto de la regla del cierre: una convención que codifica información que el compilador no da.
Y el prefijo n de los destructivos —nreverse, nconc, nsubst— es igual de sustancial: avisa de
que la estructura de entrada puede quedar destrozada, que es la diferencia entre un programa
correcto y uno con corrupción silenciosa (clase 102).
Las herramientas:
sbcl --eval '(compile-file "prog.lisp")' # los avisos del compilador SON el analizador
| Herramienta | Notas |
|---|---|
| Los avisos de SBCL | inferencia de tipos, variables sin usar, notas de optimización |
lisp-critic |
sugiere idiomas más limpios; nació para enseñar |
sblint / sbcl-lint |
los avisos en formato consumible |
(declaim (optimize (safety 3) (debug 3))) |
la política de compilación como norma |
lisp-critic es curioso y merece la mención: es un sistema experto con reglas del estilo "esto es
un (if x t nil), escribe x a secas", escrito para enseñar buen estilo Lisp a estudiantes.
Es el antepasado directo de los analizadores que sugieren idiomas, y es de los años noventa.
Ficha completa · Diseño de chips (EDA), redes, testing · tclsh prog.tcl
gets stdin linea
set p [string trim $linea]
if {$p ne "" && [string is lower -strict $p]} {
puts "valido=true"
} else {
puts "valido=false"
}
Lo que esta clase enseña en Tcl. string is lower -strict hace el trabajo entero: Tcl tiene una
familia de comprobaciones de clase —alpha, digit, integer, double, boolean, space, xdigit,
wordchar— y -strict es la parte importante: sin él, la cadena vacía devuelve verdadero.
Es una decisión de diseño discutible que ha causado muchos fallos, y por eso -strict es prácticamente
obligatorio en código serio. Es un buen ejemplo de regla de estilo con motivo.
Y Tcl tiene un estándar de estilo con nombre y con autoridad: las Tcl Style Guidelines de John Ousterhout, el creador del lenguaje, publicadas en 1997.
Y sus reglas principales siguen siendo el estándar de facto:
| Regla | Motivo |
|---|---|
Llaves siempre en expr y en if: if {$x > 5} |
sin llaves, hay doble sustitución: es un riesgo de inyección |
{*} en lugar de eval |
eval sobre datos ajenos es ejecución de código |
| Espacios de nombres para todo paquete | evita colisiones globales (clase 086) |
:: explícito para las globales |
deja claro el alcance |
Comentarios con # al principio de comando |
# a mitad de línea no es un comentario |
La primera es la más importante y merece explicarse, porque es a la vez estilo, rendimiento y seguridad:
if {$x > 5} { ... } ;# CORRECTO: la expresión se compila una vez
if "$x > 5" { ... } ;# MAL: se sustituye y se reanaliza CADA VEZ
Sin llaves, el valor de $x se pega al texto y luego se analiza como expresión. Si $x contiene
1] ; exec rm -rf / ; expr [1, eso se ejecuta.
Es una inyección de código idéntica en naturaleza a la inyección SQL (clase 153), y la regla de estilo —pon llaves siempre— es la defensa completa.
Y de paso es más rápido: con llaves, el compilador de bytecode de Tcl compila la expresión una sola vez (clase 125).
Es el mejor ejemplo de esta página de una regla de estilo que no es cosmética: protege de una vulnerabilidad y multiplica el rendimiento.
Y las herramientas:
nagelfar prog.tcl # análisis estático: aridades, comandos, citación
frink -w prog.tcl # formateo y comprobación de estilo
tclchecker # el de ActiveState
Nagelfar detecta precisamente los expr sin llaves, entre otras cosas, y es la herramienta que
convierte estas reglas en algo comprobable — que es lo que el cierre de esta clase exige.
Ficha completa · Sysadmin, texto, bioinformática · perl prog.pl
use strict;
use warnings;
my $palabra = <STDIN>;
chomp $palabra;
print "valido=", ($palabra =~ /^[a-z]+$/ ? 'true' : 'false'), "\n";
Lo que esta clase enseña en Perl. La expresión regular ^[a-z]+$ resuelve el problema en una línea,
y es el argumento de Perl entero: para trabajar con texto, la herramienta correcta es una expresión
regular (clase 093).
Y sobre estándares, Perl aportó a esta clase algo que merece contarse, porque cambió cómo la industria piensa los estándares de estilo.
En 2005, Damian Conway publicó Perl Best Practices: 256 reglas de estilo, cada una con su
justificación. Y al año siguiente, Jeffrey Ryan Thalhammer escribió Perl::Critic, un analizador
que aplica esas reglas — con dos características que hoy son estándar en todos los analizadores y
que entonces no lo eran:
Primera, severidades:
perlcritic --brutal prog.pl # nivel 1: TODO, incluso lo discutible
perlcritic --cruel prog.pl # nivel 2
perlcritic --harsh prog.pl # nivel 3
perlcritic --stern prog.pl # nivel 4
perlcritic --gentle prog.pl # nivel 5: solo lo grave (por defecto)
Que las reglas tengan severidad graduable es lo que permite adoptarlas en un proyecto existente: se empieza por lo grave y se sube el listón con el tiempo.
Y segunda, la configuración por proyecto y las excepciones justificadas:
# .perlcriticrc
severity = 3
[-Subroutines::ProhibitExplicitReturnUndef]
[Variables::ProhibitPunctuationVars]
allow = $@ $! $0
## no critic (ProhibitStringyEval)
my $r = eval $codigo; # justificado: el código viene de la configuración firmada
## use critic
La anotación en línea con el nombre de la regla concreta obliga a decir qué se está saltando, y deja constancia en el código.
Es el modelo que hoy tienen eslint-disable, # noqa, #[allow(...)] y // NOLINT, y viene de
aquí.
Y hay una regla de Perl Best Practices que merece citarse porque es la más contraintuitiva y la más útil:
Escribe las expresiones regulares con
/x, que permite espacios y comentarios dentro.
if ($fecha =~ m{
^(\d{4}) # año
-(\d{2}) # mes
-(\d{2})$ # día
}x) { ... }
Una expresión regular comentada es legible; una de sesenta caracteres no lo es. Y en un lenguaje famoso por producir código ilegible, la respuesta de su comunidad no fue prohibir la característica: fue dar una forma legible de usarla.
Es el mejor resumen de la filosofía de esta clase: los estándares no están para limitar el lenguaje, están para que el código siga siendo legible dentro de dos años.
Ficha completa · Videojuegos, navegadores, finanzas, HPC · g++ -std=c++17 prog.cpp
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
int main() {
std::string palabra;
if (!(std::cin >> palabra)) return 1;
const bool valido = !palabra.empty() &&
std::all_of(palabra.begin(), palabra.end(),
[](unsigned char c) { return std::islower(c) != 0; });
std::cout << "valido=" << (valido ? "true" : "false") << '\n';
return 0;
}
Lo que esta clase enseña en C++. El unsigned char en el parámetro de la lambda no es
decorativo: pasar un char con signo negativo a std::islower es comportamiento indefinido, y es
uno de los errores más frecuentes y menos conocidos de la biblioteca estándar de C.
Es exactamente el tipo de regla que un estándar de codificación debe recoger y una herramienta debe comprobar, porque nadie lo recuerda.
Y C++ tiene los estándares más elaborados de esta página, cada uno con su motivo:
| Estándar | Ámbito | Carácter |
|---|---|---|
| C++ Core Guidelines | general; Stroustrup y Sutter | recomendaciones, con herramienta |
| MISRA C++ | automoción | restrictivo; se prohíbe mucho |
| AUTOSAR C++14 | automoción moderna | fusionado hoy con MISRA |
| JSF++ | aviónica militar (F-35) | el más estricto: 221 reglas |
| CERT C++ | seguridad | centrado en vulnerabilidades |
| Google / LLVM style | industria | sobre todo formato y nombres |
Y el patrón común de los restrictivos merece verse, porque enseña qué se considera peligroso:
Prohibido: reserva dinámica después del arranque
Prohibido: excepciones (¡en JSF++ y en muchos sistemas embarcados!)
Prohibido: herencia múltiple de clases con implementación
Prohibido: sobrecarga de operadores salvo casos listados
Prohibido: recursión
Prohibido: goto, salvo salida de bucles anidados
Obligatorio: llaves en TODO if, incluso de una línea
"Prohibidas las excepciones" sorprende y tiene un motivo concreto: el tiempo de propagación de una excepción no está acotado, porque depende de cuántos destructores haya que ejecutar. En un sistema con plazos duros, eso es inaceptable.
Es la misma lógica que las restricciones de Ada en esta página: se renuncia a características para poder demostrar propiedades temporales.
Y las herramientas, que en C++ son de las mejores que existen:
clang-tidy --checks='cppcoreguidelines-*,modernize-*,bugprone-*' prog.cpp
clang-format -i prog.cpp
cppcheck --enable=all prog.cpp
g++ -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror
include-what-you-use prog.cpp
clang-tidy con modernize-* merece la mención final, porque hace algo poco común: reescribe el
código.
clang-tidy --fix --checks='modernize-use-nullptr,modernize-loop-convert' *.cpp
Convierte NULL en nullptr, bucles con índice en bucles por rango, typedef en using. Es
migración automatizada de estilo a escala de millones de líneas, y es la respuesta a la objeción de
que un estándar nuevo no se puede aplicar a código existente.
Estos lenguajes no pueden leer de stdin y escribir en stdout sin dejar de ser ellos
mismos. No es una limitación del material: es su naturaleza. El cálculo es el mismo y la forma
de entrar y salir es la de su anfitrión. No pasan por el verificador, y se dice.
Ficha completa · IBM i: ERP, retail, logística, manufactura · CRTBNDRPG sobre IBM i
En IBM i un programa recibe sus datos por parámetros, por un fichero o por una pantalla, nunca por la entrada estándar.
**free
ctl-opt dftactgrp(*no) actgrp(*caller);
dcl-pi ESTILO;
palabra char(60) const;
end-pi;
dcl-s texto varchar(60);
dcl-s i int(10);
dcl-s valido ind;
texto = %trim(palabra);
valido = %len(texto) > 0;
for i = 1 to %len(texto);
if %subst(texto : i : 1) < 'a' or %subst(texto : i : 1) > 'z';
valido = *off;
endif;
endfor;
if valido;
dsply 'valido=true';
else;
dsply 'valido=false';
endif;
*inlr = *on;
return;
Lo que esta clase enseña en RPG. RPG es el caso más dramático de esta página en materia de estándares de codificación, porque el estándar cambió el lenguaje entero.
Comparar las dos formas explica por sí solo lo que es un estándar de estilo:
C EVAL TOTAL = PRECIO * CANTIDAD
C IF TOTAL > 1000
C EVAL DESCUENTO = TOTAL * 0.1
C ENDIF
total = precio * cantidad;
if total > 1000;
descuento = total * 0.1;
endif;
Lo primero es RPG de formato fijo por columnas —herencia de la tarjeta perforada, igual que COBOL y Fortran en esta página—: la columna 6 es el tipo de especificación, la 7-11 el nivel, la 12-25 el factor 1, la 26-35 la operación...
Y lo segundo es el mismo lenguaje en formato totalmente libre, disponible desde 2013.
Y el estándar de la comunidad hoy es inequívoco:
| Regla | Motivo |
|---|---|
| Formato totalmente libre para todo lo nuevo | legibilidad, herramientas, git (clase 145) |
Nada de indicadores numéricos (*IN03) |
usar nombres: dcl-s salir ind |
| Procedimientos, no subrutinas | ámbito local y parámetros, en vez de globales |
| Programas de servicio para la lógica | permite pruebas unitarias (clase 139) |
dcl-s, dcl-ds, dcl-pr explícitos |
frente a las especificaciones D |
| SQL embebido en lugar de acceso registro a registro | conjuntos en vez de bucles (clase 117) |
Sin goto, sin cabxx |
de la era de los operadores de comparación con salto |
"Nada de indicadores" merece la explicación, porque es la seña de identidad del RPG antiguo: el
lenguaje tenía 99 indicadores numéricos globales —*IN01 a *IN99— que servían para todo:
condiciones, teclas de función, control de errores.
C IF *IN03
Nadie sabe qué es *IN03 sin buscar en la pantalla o en la documentación. Es el ejemplo perfecto de
una convención que el hardware impuso y que se mantuvo cuarenta años por inercia.
Y la modernización real de RPG consistió en darle nombre a las cosas — que es, reducido a lo esencial, de lo que trata esta clase entera.
Las herramientas:
| Herramienta | Qué hace |
|---|---|
| RDi / Code4i | verificador de sintaxis, formateo, navegación |
| ARCAD Observer / Transformer | conversión automática de fijo a libre |
| SonarQube (plugin RPG) | métricas y reglas |
OPTION(*SRCSTMT) |
números de sentencia del fuente en los errores |
Sí podrían cumplir el contrato, pero su cadena de herramientas no está en los runners de CI.
Ficha completa · Mainframe z/OS: banca, seguros · IBM Enterprise PL/I for z/OS
estilo: procedure options(main);
declare palabra char(60) varying;
declare i fixed binary(31);
declare valido bit(1) initial('1'b);
declare c char(1);
get edit (palabra) (a(60));
palabra = trim(palabra);
if length(palabra) = 0 then valido = '0'b;
do i = 1 to length(palabra);
c = substr(palabra, i, 1);
if c < 'a' | c > 'z' then valido = '0'b;
end;
if valido then
put skip list ('valido=true');
else
put skip list ('valido=false');
end estilo;
Lo que esta clase enseña en PL/I. PL/I es el lenguaje que mejor ilustra por qué existen los estándares de codificación restrictivos, porque es el caso de estudio del problema opuesto: un lenguaje que lo permite todo.
PL/I se diseñó en 1964 para unificar el mundo científico de Fortran y el comercial de COBOL, y añadió además concurrencia, procesamiento de listas, manejo de excepciones y programación de sistemas.
El resultado fue enorme, y la reacción de la comunidad académica fue igual de célebre: Edsger Dijkstra escribió que era un lenguaje "demasiado barroco para ser dominado" y Niklaus Wirth diseñó Pascal en gran medida como respuesta — un lenguaje pequeño, con una sola forma de hacer cada cosa.
Pascal y PL/I son las dos filosofías de esta clase en estado puro, y las dos siguen vivas: los lenguajes que dan un camino y los que dan veinte.
Y la solución práctica en PL/I fue la de esta clase: subconjuntos de uso obligatorio.
Estándares típicos de una instalación PL/I:
- Prohibido DEFAULT: declarar TODO explícitamente
- Prohibido el alias por DEFINED y BASED salvo casos aprobados
- Prohibidas las conversiones implícitas: conversión explícita siempre
- Un solo punto de retorno por procedimiento
- Prefijos de condición obligatorios: (SUBSCRIPTRANGE, STRINGRANGE, SIZE)
- Nada de GOTO fuera del bloque
"Prohibido DEFAULT" merece la explicación, porque es la característica más peligrosa del lenguaje:
default range(a:z) fixed binary(31); /* TODO lo no declarado es entero */
DEFAULT permite redefinir las reglas de tipos implícitos para todo un programa. Es potentísimo y
convierte el código en ilegible para quien no vio esa línea — el mismo problema que el implicit de
Fortran en esta página, elevado a norma configurable.
Y las opciones del compilador que aplican el estándar:
PP(MACRO) FLAG(W) RULES(NOLAXDCL, NOLAXCTL, NOLAXIF, NOLAXQUAL)
RULES(NOLAXDCL) exige que todo esté declarado, NOLAXIF prohíbe las comparaciones laxas y
NOLAXQUAL obliga a cualificar los nombres de estructura.
Es implicit none y use strict de esta página, en un compilador de IBM, aplicable por opción de
compilación — y es la forma correcta de imponer un estándar: que no compile.
Ficha completa · Sanidad: historia clínica, VistA, Epic · YottaDB
ESTILO ; Validar identificador -- clase 146
read palabra
new i, c, valido
set valido = $select($length(palabra) > 0 : 1, 1 : 0)
for i = 1:1:$length(palabra) do
. set c = $extract(palabra, i)
. if (c < "a") ! (c > "z") set valido = 0
write "valido=", $select(valido : "true", 1 : "false"), !
quit
Lo que esta clase enseña en M. Aquí está el caso que el gancho de la clase anunciaba: la convención más importante de M nació de una limitación física.
El estándar de M limitaba los nombres a ocho caracteres —y en la práctica, muchas implementaciones solo distinguían los primeros ocho—. Además, el espacio de rutinas era plano y global: no hay espacios de nombres.
Y de ahí salió la convención que define el código VistA y que merece explicarse, porque es una solución ingeniosa a un problema real:
DPT -- fichero de pacientes
XU -- Kernel (utilidades del sistema)
XUS -- Kernel, seguridad
DI -- FileMan
LR -- laboratorio
PS -- farmacia
RA -- radiología
Cada paquete tiene un prefijo de dos o tres letras asignado formalmente, y todas sus rutinas, globals y variables empiezan por él.
do EN^PSOORDER ; punto de entrada EN de la rutina PSOORDER (farmacia)
set ^PSDRUG(ien, 0) ; la global de medicamentos
Eso es un espacio de nombres implementado con una convención de nombres, administrado por un registro central, y funcionando en un sistema de decenas de miles de rutinas.
Es la respuesta más pura de esta página a la pregunta de qué es un estándar de codificación: una convención que sustituye a una característica que el lenguaje no tiene.
Y VistA tiene el documento que lo formaliza, y merece nombrarse: el SAC, VistA Programming Standards and Conventions, que además de los prefijos regula:
| Regla | Motivo |
|---|---|
new obligatorio para toda variable local |
el ámbito es global por defecto (clase 088) |
Nada de $zx específico del fabricante en código portable |
funciona en varias implementaciones |
Puntos de entrada documentados, con ;; |
la línea de doble comentario es la interfaz |
Nada de kill sin argumentos |
borraría todas las variables del proceso |
$$ para funciones extrínsecas |
distingue función de procedimiento |
Nada de indirección ni xecute sin justificar |
imposible de analizar (clase 123) |
La primera es la más importante y la más peligrosa de olvidar: en M, una variable no declarada es
global al proceso, así que una rutina que usa I como contador sin hacer new I destruye el
contador de quien la llamó.
Es el mismo fallo que las variables globales de cualquier lenguaje, agravado porque aquí es el comportamiento por defecto.
Y por eso la regla del SAC es tajante y la revisión de código de VistA la comprueba siempre: new
todo lo que uses. Es la convención que hace posible que miles de rutinas de decenas de paquetes
convivan en un espacio de variables compartido.
Ficha completa · Banca, seguros, trading · Pharo
| palabra valido |
palabra := stdin nextLine trimBoth.
valido := palabra notEmpty and: [
palabra allSatisfy: [ :c | c isLowercase ] ].
Transcript show: 'valido=', (valido ifTrue: [ 'true' ] ifFalse: [ 'false' ]); cr.
Lo que esta clase enseña en Smalltalk. allSatisfy: con un bloque es el every de Lisp y el
all_of de C++ de esta página, y and: con un bloque es la conjunción perezosa: el segundo
argumento solo se evalúa si el primero es verdadero (clase 084).
Y sobre estilo, Smalltalk tiene las convenciones más peculiares de esta página, porque la sintaxis de mensajes con palabras clave convierte los nombres en frases:
cuenta transferir: 100 desde: origen a: destino.
coleccion detect: [ :x | x > 5 ] ifNone: [ nil ].
El selector completo es transferir:desde:a:, y las convenciones giran alrededor de eso:
| Convención | Ejemplo |
|---|---|
| El selector debe leerse como una frase | at:put:, no set:with: |
Predicados con is o adjetivo |
isEmpty, notNil, includes: |
Devolver self en los que modifican |
permite encadenar con ; |
| Los métodos, cortos | tres a siete líneas es lo habitual |
| Categorías (protocolos) para agrupar | accessing, printing, private |
| Comentario de clase obligatorio | explica el propósito, no la implementación |
"Los métodos cortos" no es una recomendación blanda en Smalltalk: es cultural y muy estricta. Un método de treinta líneas se considera un defecto, y la razón es concreta: el navegador muestra un método a la vez, así que un método que no cabe en la ventana es un método que no se puede leer de una vez.
Es un caso claro de una convención de estilo formada por la herramienta, igual que el ancho de 80 columnas viene del terminal y las ocho letras de M vienen del estándar.
Y las herramientas del ecosistema, que aquí son especiales por la razón de siempre —el sistema se analiza a sí mismo:
| Herramienta | Qué hace |
|---|---|
| SmallLint / Code Critics | reglas de estilo y defectos, integradas en el navegador |
allCallsOn: |
quién llama a un selector (clase 138) |
| Refactoring Browser | renombrar, extraer método, mover, con seguridad |
| Metrics / Moose | métricas y análisis de arquitectura sobre el sistema vivo |
SmallLint merece la mención final, porque hace algo que muy pocos analizadores hacen: avisa mientras escribes, dentro del navegador de clases, y ofrece la corrección aplicable con un clic.
Y la razón por la que puede hacerlo es la de la Parte 8: el código es un objeto, así que el analizador es un programa que recorre objetos — no un analizador de texto que reimplementa el lenguaje.
Es la conclusión de esta clase en su forma más limpia: cuando el entorno entiende el código, la comprobación mecánica es barata y automática, y la revisión humana queda libre para lo que solo una persona puede juzgar.
Lo transferible: todo lo que una máquina pueda comprobar, debe comprobarlo la máquina — formato, nombres, complejidad, patrones peligrosos, cobertura. Lo que queda para la revisión humana es lo único que una herramienta no puede juzgar: si el código resuelve el problema correcto, si el diseño aguantará el próximo cambio, y si alguien que llegue dentro de dos años lo entenderá. Una revisión que discute sangrados está desperdiciando lo más caro del proceso, que es la atención de otra persona. Y una regla que no se puede automatizar y nadie recuerda, no es un estándar: es una aspiración.
⏮️ Volver a la clase 146 · 🧬 Los primos del Atlas · 🧟 Índice de lenguajes vivos