El futuro del paradigma de programación: hacia lenguajes intencionales

La forma en que veo la programación es diferente a lo que era la semana pasada debido a diversos factores. Primero, he pasado mucho tiempo últimamente con mi cabeza sumergida en la documentación de .NET, y algo en el fondo de mi mente decía: "Si C# es mucho más eficiente que VB.NET, ¿por qué todos los ejemplos de código parecen tener la misma longitud y lucen virtualmente idénticos?". Luego, releí un correo electrónico de alguien que me decía que el lenguaje XYZ era increíble porque necesitaba muchísimas menos líneas de código fuente (SLOC) para hacer lo mismo que los lenguajes más populares. Y, aún sigo trabajando en el mismo pequeño y tonto pedazo de código haciendo multihilo. Finalmente, leí una publicación de Mark Miller sobre Emacs y Lisp.

Todo esto se está conectando ahora, mi comprensión consciente de lo que realmente no me gusta de la programación. Me metí en la programación porque me gusta resolver problemas, pero resolver problemas es realmente solo una pequeña fracción del trabajo. El resto es dar detalles exactos y precisos sobre cómo realizar las tareas que resuelven los problemas. Es como pintar la Capilla Sixtina poniendo a un niño de siete años en un andamio y dictándole cada pincelada que quieres que haga. Es ridículo.

Esto pone de relieve el verdadero problema en la industria de la programación: Todo lo que agregamos al proceso de desarrollo es basura añadida a una base podrida. Veamos más de cerca lo que hacemos aquí, amigos, y examinemos cuánto ha cambiado (o no) el paradigma de la programación.

Índice de Contenido
  1. Antes (hace 10-20 años)
  2. Ahora
  3. ¿Hacia dónde vamos desde aquí?

Antes (hace 10-20 años)

Un programador se sienta y analiza el proceso de trabajo. Usando una plantilla y un lápiz, traza un flujo de trabajo en papel cuadriculado. El programador se sienta, enciende su confiable editor de texto (si tiene suerte, admite autoindentación) y crea uno o más archivos de texto que contienen código fuente. Luego, compila el código fuente (o trata de ejecutarlo a través de un intérprete) para verificar la corrección sintáctica. Una vez que compila o se interpreta correctamente, lo ejecuta con casos de prueba para verificar la corrección lógica y buscar errores en tiempo de ejecución. Cualquier error se registra en un archivo de texto; o tal vez tenga un depurador o analizador de volcado de errores para averiguar qué salió mal. Esto continúa hasta que el programa se considera completo.

Ahora

Un programador se sienta y analiza el proceso de trabajo. Utilizando una herramienta de diagramas de flujo o tal vez un editor de UML, traza un flujo de trabajo. El programador se sienta, enciende su confiable entorno integrado de desarrollo (si tiene suerte, admite control de versiones) y crea uno o más archivos de texto que contienen el código fuente. Algunas partes de este código pueden ser generadas automáticamente, como los objetos de datos basados en el esquema de la base de datos o algunos códigos básicos del UML. La mitad de este código generado deberá descartarse a menos que el proyecto sea muy básico, pero al menos es un comienzo. El entorno de desarrollo integrado se encarga de los conceptos básicos de mostrar un formulario, ya sea una aplicación de escritorio o una aplicación web. El programador luego compila el código fuente (o trata de ejecutarlo a través de un intérprete) para verificar la corrección sintáctica. Una vez que compila o se interpreta correctamente, lo ejecuta con casos de prueba para verificar la corrección lógica y buscar errores en tiempo de ejecución. Cualquier error se registra en un archivo de texto; o tal vez tenga un depurador o analizador de volcado de errores para averiguar qué salió mal. Esto continúa hasta que el programa se considera completo.

Wow... hemos cambiado vi por Visual Studio y una serie de instrucciones de impresión por [F11]. Al final del día, nada ha cambiado realmente excepto por las herramientas, las cuales apenas están a la altura de la creciente complejidad del desarrollo de software. Estamos atrapados, y estamos en una situación muy mala.

Alerta de vulnerabilidad en el entorno de ejecución Java Runtime Environment

A los defensores de Lisp les gusta decir: "¡Lisp habría conquistado el mundo si solo tuviéramos mejores/diferentes hardware!". Ahora tenemos el hardware, y Lisp todavía no ha conquistado el mundo. Afirmo que necesitamos ir mucho más allá de algo como Lisp en este momento; necesitamos entrar en un tipo completamente nuevo de lenguaje. Un gran ejemplo de esto es el multihilo.

Por lo general, los profesionales de TI dicen dos cosas sobre llevar el trabajo de multihilo al ámbito principal: los compiladores necesitan volverse mucho más inteligentes antes de que esto pueda suceder y no podemos hacer que los compiladores sean lo suficientemente inteligentes para que esto funcione bien. Si esto parece una contradicción irresoluble, no lo es. El problema no son los compiladores, son nuestros paradigmas.

Si has pasado una buena cantidad de tiempo con el multihilo, sabrás que nunca se convertirá en una técnica común hasta que el compilador pueda manejar la mayor parte de él automáticamente. El hecho es que es complicado para la mayoría de los programadores mantener los detalles de un sistema cuando la concurrencia es un problema, y depurar estos sistemas es un verdadero terror.

Los compiladores más inteligentes son la respuesta, pero existe una verdad entre los expertos en compiladores: los compiladores nunca serán tan inteligentes. ¿Por qué? Porque traducir adecuadamente una operación en serie en una paralela requiere entender las intenciones del código y no solo el paso a paso de cómo sería el código fuente. Puedo leer tu código y (si es lo suficientemente claro) entender probablemente el 80% de tus intenciones de antemano y posiblemente descifrar un 10% o 15% adicional de tu código con el tiempo. El otro 5% o 10% será un misterio hasta que te pregunte.

Lee los comentarios de esta publicación de Chad Perrin sobre Python y Ruby; estas personas están tratando de optimizar un fragmento trivial de código. Lo que es aún más interesante es el punto que Chad hace una y otra vez en sus respuestas (publica como apotheon): Tal vez la intención del código era producir ese contador en pantalla que se elimina en la mayoría de las optimizaciones. O tal vez, a pesar de que es más lento debido a la concatenación de cadenas inmutables, también había un efecto secundario. ¿Quién sabe? Las intenciones no se pueden entender a través del código sin tener comentarios ridículamente detallados en el código o contar con una especificación de producto exhaustiva.

Parece que Perl entiende las intenciones, pero es un truco. Perl simplemente fue diseñado para hacer ciertas suposiciones basadas en la forma en que la mayoría de los programadores suelen pensar en ausencia de un código lógico. Si tus intenciones no concuerdan con las suposiciones de Perl, o bien no se ejecutará o lo hará de manera incorrecta. Si no me crees, intenta abusar de Perl un poco con un formato de registro que especifica la tilde (~) como separador de registro en lugar de una nueva línea. Las suposiciones de Perl se desvanecen a menos que establezcas la cadena de separación de registros en tu código; y en ese punto, ya no estás realmente utilizando las suposiciones, sino que estás dando instrucciones precisas.

La Edición Estándar de Java (SE) reemplazará gradualmente a la Edición Micro de Java (ME)

Estoy a favor del código autoexplicativo. Me gusta pensar que nadie que lea mi código tuvo que esforzarse para averiguar qué hace. Pero aparte de las especificaciones de diseño y/o páginas de comentarios en línea, no hay forma de que alguien que lo lea sepa por qué escribí el código de la forma en que lo hice. Es como estar en el ejército. Por lo general no te dicen por qué necesitas marchar por un camino, simplemente te dicen que lo hagas.

Los programadores están en un dilema. Sin una forma para que el compilador conozca las especificaciones de diseño, el compilador no puede optimizar verdaderamente algo tan complejo como una operación paralela más allá de las optimizaciones realmente obvias. No hay reglas fijas para convertir una operación en serie en una paralela; es un arte oscuro que implica mucha prueba y error incluso para los practicantes frecuentes. Para que el compilador pueda comprender esas especificaciones de diseño, requiere un tipo de lenguaje y compilador completamente diferente. Las aserciones y otros elementos de "diseño por contrato" no son ni siquiera la punta del iceberg, son la gaviota posada en el iceberg en términos de resolver el problema. Si el compilador es lo suficientemente inteligente como para entender el documento de diseño, ¿por qué molestarse en escribir el código? Las herramientas de UML a código generalmente intentan hacer esto, pero no van lo suficientemente lejos. En el mejor de los casos, estas herramientas pueden traducir un proceso en código que literalmente exprese ese proceso; no hay comprensión de la intención.

Hay algunas grandes corrientes de lenguajes de programación: orientados a objetos, procedurales, funcionales e imperativos, todos los cuales tienen sus fortalezas y debilidades. Los lenguajes funcionales e imperativos se acercan más a lo que estoy hablando. SQL es un gran ejemplo. Le dices "qué quieres", no "cómo obtener lo que quieres". Los detalles sobre el "cómo" quedan a cargo del motor de la base de datos. Como resultado, las bases de datos están optimizadas de tal manera que funcionan mucho más rápido que lo que el 99.9% de los programadores podrían hacer con el mismo código (sin mencionar la redundancia, las transacciones, etc.), a pesar de su naturaleza bastante general. Incluso SQL tiene muchas debilidades; aunque es un lenguaje imperativo, es extremadamente específico del dominio. Tan pronto como quieras manipular esos datos, te adentras en el mundo del código procedural, ya sea dentro de un procedimiento almacenado o en tu aplicación.

La mayoría de las aplicaciones (y todas las bibliotecas) se convierten en un lenguaje específico de dominio en sí mismas, no sintácticamente (aunque algunas, como las bibliotecas de expresiones regulares, alcanzan ese estatus), sino en términos de terminología, tema, etc. El problema que probablemente intentas abordar no tiene absolutamente nada que ver con el lenguaje que estás utilizando y todo que ver con un problema específico no relacionado con la programación.

Un gran ejemplo de la desconexión entre el código y el problema que aborda es el tema de los bucles. Apuesto a que más del 50% de los bucles por ahí recorren una colección de datos completa y no se detienen sin haber tocado cada elemento de la colección. La lógica que estamos implementando es algo como "encuentra todos los elementos en la colección que cumplan la condición XYZ" o "haz la operación F en todos los elementos de la colección". Entonces, ¿por qué estamos trabajando con lenguajes que no tratan con la teoría de conjuntos?

Los programadores terminan implementando lo mismo una y otra vez (es decir, una función u objeto o cualquier otra cosa en una biblioteca que toma ese conjunto grande como entrada) e implementando un comando de filtro que devuelve un conjunto más pequeño y filtrado, o un método "EjecutarEnTodos" que toma un puntero a una función como argumento.

Programación en Haskell: Utilizando listas infinitas para simplificar y optimizar el código

Cuando los expertos en lenguajes intentan facilitarnos las cosas, el resultado es cosas como LINQ, la combinación de un DSL (lenguaje específico del dominio) con un marco de trabajo o lenguaje para compensar las deficiencias de ese lenguaje o marco en un dominio de problemas específico.

La única forma en que LINQ podría implementarse es si los lenguajes que lo admiten tienen clausuras, que son "la cosa" en un lenguaje funcional como Lisp; las clausuras también son importantes en Perl. Pero los programadores han estado trabajando en Java y en lenguajes similares a Java durante los últimos cinco a diez años (dependiendo de cuánto fueran adoptantes tempranos), y muchos de ellos se perdieron por completo Perl, ya sea quedándose con VB y luego con los lenguajes .NET o comenzando con C/C++ (o Pascal) y pasando a Java y/o .NET. La única exposición que la mayoría de los programadores han tenido a un lenguaje verdaderamente dinámico es JavaScript (también conocido como ECMAScript). (Con lenguaje dinámico, me refiero a un lenguaje que puede modificarse o implementar o volver a implementar la funcionalidad durante el tiempo de ejecución. JavaScript, con su compatibilidad con eval(), tiene esta capacidad y también Perl. En lenguajes funcionales como Lisp, esto es todo lo que el lenguaje realmente puede hacer). En realidad, JavaScript no es tan malo; de hecho, me gusta bastante. Simplemente no soy un gran fanático del entorno y el modelo de objeto con el que los programadores están acostumbrados a verlo (el DOM del navegador).

La mayoría de los programadores con los que he tratado tienen poca (o ninguna) experiencia con un lenguaje dinámico. El resultado es que no tienen la capacidad de pensar de la manera que se necesita en un lenguaje dinámico. Estos programadores andan a tientas a ciegas; tienen una intuición de que no están trabajando a su potencial máximo, pero no tienen idea por dónde empezar. (¿Por qué sino habría un enorme mercado de generadores de código y otros gadgets para mejorar la productividad reduciendo la cantidad de escritura?)

No creo que Lisp y sus similares sean la solución; son demasiado difíciles para que la mayoría de los programadores los lean y los comprendan, y todo el código es una referencia cruzada a sí mismo. Los lenguajes procedurales también han demostrado ser difíciles de mantener. Los lenguajes orientados a objetos son los más fáciles de mantener, a expensas de ser extremadamente verbosos, lo que es otra razón para los generadores de código. Los lenguajes imperativos casi siempre serán poco más que DSL.

¿Hacia dónde vamos desde aquí?

Quiero ver a los programadores dirigirse hacia lenguajes intencionales. No tengo idea de cómo se verían, pero el código fuente tendría que ser mucho más que el archivo de texto habitual y tendría que poder expresar relaciones dentro del formato de datos, como lo hacen XML o una base de datos. El paradigma de programación debe poder reducir rápidamente cualquier problema empresarial a un algoritmo para que el trabajo real se pueda dedicar a escribir el algoritmo y luego, en el último momento, traducir los resultados a las estructuras de datos del dominio nuevamente. El paradigma de programación se vería como una mezcla de muchas ideas diferentes, pero la sintaxis y la puntuación serían bastante irrelevantes.

¿Qué piensas sobre el paradigma de programación? ¿Hacia qué dirección crees que debe tomar?

Cómo generar archivos PDF dinámicamente con PHP utilizando FPDF

J.Ja

En Newsmatic nos especializamos en tecnología de vanguardia, contamos con los artículos mas novedosos sobre Desarrollo, allí encontraras muchos artículos similares a El futuro del paradigma de programación: hacia lenguajes intencionales , tenemos lo ultimo en tecnología 2023.

Artículos Relacionados

Subir

Utilizamos cookies para mejorar su experiencia de navegación, mostrarle anuncios o contenidos personalizados y analizar nuestro tráfico. Al hacer clic en “Aceptar todo” usted da su consentimiento a nuestro uso de las cookies.