sábado, agosto 04, 2007

El lenguaje de programación universal

El mundo de la informática se ha vuelto sumamente complejo hoy en día, cientos de librerías, docenas de lenguajes de programación, estas variables hacen del desarrollo de software una pesadilla. sería muy interesante tener un único (o al menos principal) medio de expresión del software, que la gran mayoría aceptara como la manera de comunicarse con las máquinas.

Vamos a explorar una de las maneras de atacar el problema de la complejidad del software, intentaremos crear un lenguaje ideal, el lenguaje de nuestros sueños, que ofrezca todos los mecanismos que nos permitan expresarnos de la manera que mejor nos parezca para resolver un problema dado, un lenguaje que soporte todos los paradigmas actuales de la programación. Ante todo, nuestro lenguaje debe soportar la programación estructurada, la programación orientada a objetos (OOP) en todas sus variantes, la programación funcional y la programación concurrente.

Deberíamos ser incluso más generales, diseñaremos un lenguaje sencillo pero extensible, así, podríamos partir de una base extremadamente simple, que nos permita implementar nuevas funcionalidades o extensiones a partir de la misma, así además de lograr implementar los paradigmas actuales de la programación, tendremos una muy buena oportunidad de implementar los paradigmas que se inventen en el futuro, cuando estos aparezcan.

Como nuestro lenguaje pretende ser extensible lo llamaremos LPX por las siglas de Lenguaje de Programación eXtensible, que no es muy original pero captura la idea.

Queremos que LPX no solo sea una moda, que sea la sólida base para una plataforma que dure al menos hasta que las máquinas adquieran la suficiente inteligencia para describirles los problemas y que ellas mismas nos den la solución.

Deseamos que LPX sea adoptado ampliamente y por ello debemos diseñarlo para que sea fácil de aprender y muy fácil de leer incluso para personas sin nociones de programación, permitiendo a los usuarios con un mínimo entrenamiento hacer modificaciones sencillas a programas existentes. Tal vez esto parezca una utopía, sin embargo, son los usuarios prácticamente sin entrenamiento, los que universalmente extienden Excel haciendo macros en Visual Basic para Aplicaciones (VBA), LPX debería ser más fácil de aprender que VBA.

LPX será mejor si tiene pocos elementos de sintaxis, evitaremos la mayor cantidad posible de signos de puntuación, palabras reservadas, y reglas de construcción de oraciones. Por ello quedan descartados rápidamente lenguajes como Perl (¡que lastima!) y C++, mientras que lenguajes como Ruby, Java y Pascal podrían ser opciones viables, aunque intentaremos que sea aún mas simple.

Un lenguaje que se simplifica la sintaxis al máximo es Python, y de hecho podría servirnos, si no fuera por su esquema de sintaxis diagramada, donde los espacios en blanco especifican la estructura del programa, es antinatural para casi cualquier persona, además complica las herramientas, pues hasta el compilador tiene problemas con esto, así que lo descartaremos.

Copiar una sintaxis preexistente es lo más fácil cuando se quiere diseñar un nuevo lenguaje, porque se puede reutilizar el código de las herramientas existentes y hasta parte de la plataforma operacional; además es importante que nuestro lenguaje sea fácil de manipular para facilitar el desarrollo de herramientas como IDEs y generadores de código que son un factor importante en la adopción del lenguaje.

Aunque pareciera que nos estamos quedando sin opciones, hay todavía una sintaxis sencilla, que tiene poquísimas reglas de construcción, es fácil de manipular automáticamente, está ampliamente difundida y es extensible: ¡ XML !. Si además evitamos los atributos en las marcas de XML y algunas otras características exóticas, su aprendizaje se reduce a saber que una marca se arma con <nombre>contenido</nombre> como ¡ única regla de sintaxis !.

Debo desviarme un instante de la discusión para advertir que estoy muy consciente de que hay mucho más que sintaxis en la barrera de adopción de un lenguaje, la semántica (el significado) también es importante, y por ello LPX tendrá las capacidades de cualquier lenguaje procedimental (asignación y secuencia) y también tendrá funciones de orden superior (higher order functions), que es solo un nombre rimbombante para las funciones que reciben, manipulan o retornan otras funciones, por ejemplo en Perl (hubiera preferido poner un ejemplo en Java, pero no tiene esta capacidad, afortunadamente hay lenguajes más útiles):

sub fold { my ($func, $valor, $lista) = @_;
for my $elemento ( @$lista ) {
$valor = $func->($elemento, $valor)
}
$valor;
}

my $sumar = sub { my ($a,$b) = @_; $a + $b };
my $multiplicar = sub { my ($a,$b) = @_; $a * $b };

print fold($sumar, 0, [1, 2, 3, 4, 5]), "\n";
print fold($multiplicar, 1, [1, 2, 3, 4, 5]), "\n";

La función fold recibe una función, un valor inicial y una lista para aplicar la función a todos los elementos de la lista para lograr una acumulación, así la última línea del ejemplo calcula el producto de los elementos en la lista y la penúltima calcula la suma.

Espero que el ejemplo deje claro que está técnica es muy útil aunque solo sea para reutilizar código, pero también ilustra porque Java es un lenguaje limitado y porque la sintaxis de Perl fue la primera en ser descartada.

Habiéndome cuidado de que me reclamen los conocedores, ahora solo debemos hacernos una idea de como luce nuestro lenguaje.

Una expresión como a+b*15 sería algo como:

<sum>
<var>a</var>
<mult>
<var>b</var>
<num>15</num>
</mult>
</sum>

Una llamada a una función:
<factorial>
<param><num>10</num></param>
</factorial>

Ummm??, me da la impresión que el lenguaje es fácil de entender, sin embargo, está algo sobrecargado, además estamos introduciendo muchas palabras, vamos a hacer la función factorial para ver cómo luce:

<define>
<func>factorial</func>
<param>n</param>
<body>
<if>
<cond><lt><var>n</var><num>2<num></lt></cond>
<then>
<return><num>1</num></return>
</then>
<else>
<return>
<mult><var>n</var>
<factorial>
<subst><var>n</var><num>1<num></subst>
</factorial>
</mult>
</return>
</else>
</if>
</body>
</define>

Definitivamente sufrimos del problema de siempre en XML, las marcas terminan resaltando mucho más que los datos (en este caso, el algoritmo). Podríamos remediar parte del problema si elimináramos algunas de las marcas complicando algo nuestro compilador para reconocer números, nombres y algunas secuencias de construcciones separadas por espacios en blanco. Así una expresión como:

<lt><var>n</var><num>2</num></lt>

se podría simplificar a:

<lt>n 2</lt>

Sin embargo nos interesa mantener esta última estructura, pues de este modo <lt> puede ser un operador incluido en el lenguaje o una llamada a una función como en: <factorial>n</factorial>, lo que nos permitirá extender el lenguaje ad infinitum, agregando funciones. También eliminaremos la necesidad de especificar el valor de retorno y asumiremos que el último valor evaluado por una función es el valor de retorno (tenia que dejar algo de Perl). Veamos como luce ahora LPX:

<define>
<func>factorial n</func>
<body>
<if>
<cond><lt>n 2</lt></cond>
<then>1</then>
<else>
<mult><var>n</var>
<factorial>
<subst>n 1</subst>
</factorial>
</mult>
</else>
</if>
</body>
</define>

Con estos cambios hemos perdido algo de la flexibilidad de XML, y la posibilidad de procesar todos los aspectos de nuestro programa con las herramientas existentes. Sin embargo, todavía LPX es fácil de procesar automáticamente, aunque desafortunadamente no tan fácil de aprender y menos de leer. Entre otras, no hemos eliminado todas las secuencias. Por ejemplo si asumimos que <if> siempre tiene tres elementos (cond, then, else) separados por blancos, podríamos simplificar aún más el lenguaje a costa de introducir secuencias.

Una vez comprobado que perder algunos beneficios puede hacer LPX más transparente. Podemos transformar las marcas de XML, para escribirlas de forma diferente. Así, en vez de escribir <marca>contenido</marca>, escribiremos <marca contenido>, de esta manera se mantienen las propiedades de facilidad de procesamiento y la claridad aumenta notablemente:

<define <factorial n>
<if <lt n 2>
1
<mult n <factorial <subst n 1>>>
>
>

Ahora sí estamos avanzando, se ve sencillo, fácil de aprender y de leer, sin embargo, sería interesante poder utilizar marcas que no fueran nombres, sino símbolos. Esto permitiría escribir:

<mult n <factorial <subst n 1>>>

de una manera más natural:

<* n <factorial <- n 1>>>

Claro que ya no sería tan fácil con <lt n 2> pues <<n 2> se prestaría a confusión, pero esto se arregla fácilmente reemplazando '<' y '>' por '{' y '}', quedando así:

{define {factorial n}
{if {< n 2}
1
{* n {factorial {- n 1}}}
}
}

Perfecto, fácil de entender, fácil de aprender, fácil de extender infinitamente y fácil de manipular. Ahora solo tenemos que ponernos a trabajar en un compilador, un interpretador y comenzar a usarlo en todos partes.

Pero no nos apresuremos, se me ocurre una idea para facilitarnos el trabajo (luego les digo porque), en vez de usar '{' y '}' podríamos utilizar '(' y ')':

(define (factorial n)
(if (< n 2)
1
(* n (factorial (- n 1)))
)
)

Creo que me gustaba más como se veía anteriormente, aunque esto puede ser simplemente porque antes se parecía más a Perl (a estas alturas se deben imaginar que me gusta Perl). Pero, como dije anteriormente, el cambio no fue un capricho, estaba esperando reutilizar algunas herramientas ya existentes.

En efecto, ¡ ya ese lenguaje existe !

Bienvenidos a Lisp, un lenguaje sencillo, fácil de aprender y manipular automáticamente, creado en 1958. Tiene hoy la misma vigencia de siempre, la forma de su sintaxis ha permitido el desarrollo de herramientas de procesamiento que le dan la capacidad de extenderse a sí mismo, y adaptarse a cualquier paradigma inventado o por inventar.

Caracterizado como ``el lenguaje de programación programable'', Lisp ha sido capaz de adaptarse a nuevos paradigmas de programación, debido a su capacidad de extenderse a sí mismo, así es como hoy en día maneja objetos mejor que casi cualquier otro incluidos Java y C++, aunque la aparición de la programación orientada a objetos sucede casi tres décadas después de la invención de Lisp.

En Lisp se utilizaba la orientación a aspectos utilizando funciones de orden superior antes de que se inventara la programación estructurada, pero además las funciones de orden superior logran que ese concepto sea tan natural, que ningún programador de Lisp puede entender como alguien puede ser capaz de soportar cosas como AspectJ.

En Lisp se hacían programas genéricos décadas antes de que la programación genérica se pusiera de moda. Las plantillas de C++ palidecen con respecto al sistema de metaprogramación (macros) de Lisp, que permite extender el lenguaje prácticamente sin ningún límite, y los macros ya eran populares para cuando se conciben las plantillas de C++.

Lisp puede manipular listas de elementos de forma simple, efectiva y eficiente, en efecto su nombre viene de LISt Processor, y las listas en Lisp se representan como elementos encerrados entre paréntesis:

(1 2 3 esta es una lista en lisp 4 5 6)

Un programa en Lisp, es también una lista de Lisp, así que procesarse a si mismo es muy natural, una característica que que ha sido mucho valor en el campo de la inteligencia artificial.

Lisp se puede compilar eficientemente a lenguaje de máquina, logrando un rendimiento y tamaño similar al de programas equivalentes de lenguaje C, pero es muy fácil de interpretar con una sencilla máquina virtual, que podría funcionar con holgura en cualquier reloj de pulsera.

Lisp permite implementar concurrencia eficiente y segura con mínimas modificaciones al lenguaje (vean Termite, o mejor aún, pruebenlo).

A finales de los 80 cuando la gente se da cuenta de que los punteros son malos, en vez de mirar a los lados, o incluso hacia atrás, se ponen a hacer algo completamente nuevo, el gran drama del ``no se inventó aquí'', así nace Java, un lenguaje moderno cuyas características más resaltantes para la época eran:

  1. Liberar al programador del manejo explicito de la memoria y

  2. La independencia de la plataforma

Dios mio! Lisp ya tenía 25 años con esas características cuando los diseñadores de Sun comenzaron a concebir Java.

Cuando Netscape decide agregar programas a las páginas web desarrollaron Javascript, ignorando que en esa época, el lenguaje para procesar HTML era DSSSL, que es prácticamente un subconjunto de Lisp.

La mayoría de la gente opina que XML ha sido un gran avance para la informática porque permite la expresión de cualquier jerarquía de datos con mucha facilidad y flexibilidad, estas ventajas evidentes solo son comparables con la ofuscación que le produce a los datos mismos, ¿recuerdan nuestro primer intento de una sintaxis para LPX?, sin embargo XML y las listas de Lisp son isomorfas (una palabrota para decir equivalentes), pero con una sintaxis mucho más sencilla. Si todo el sistema de XML se hubiera basado en las expresiones S (las listas de Lisp), la mayoría de las herramientas hubieran estado listas antes de comenzar el diseño.

No puedo imaginar la cantidad de enredos, dificultades e ineficiencias que ha provocado la combinación XML/javascipt, sobre todo cuando ya existía una plataforma, simple y eficiente para manejar todo.

En publicaciones recientes como CREST (Computational REST), los investigadores "descubren" que los sistemas distribuidos se pueden ver como una sola aplicación donde el estado de la misma se distribuye mediante continuaciones, mientras hay sistemas en Lisp que llevan años utilizando esos mecanismos.

Durante el desarrollo de las bases de datos relacionales, se inventó SQL (en 1974) como lenguaje fácil para efectuar consultas, pero tomando en cuenta que una entidad (tabla) no es más que una lista de tuplas (registros) que a su vez son listas de elementos (campos), deberíamos suponer que Lisp hubiera sido ideal para hacer el trabajo, pues si lo era, y todavía lo es, pero de haberlo usado como lenguaje de consultas, hoy utilizaríamos el mismo lenguaje para consultar bases de datos relacionales, bases de datos jerárquicas y bases de datos orientadas a objetos con la misma interfaz, da tristeza lo que pudo haber sido y no fue.

Aún hoy se discute como agregar clausuras, y concurrencia eficiente y segura en Java, cuando Lisp tenía estas capacidades para el momento en que Java estaba siendo diseñado.

Y entonces, ¿por qué la gran mayoría de los programadores usa C++, Java o lenguajes similares aunque probablemente mejores?, la verdad es que solo puedo pensar en tres razones:
  1. Ignorancia: aunque me parece poco creíble en vista de la amplitud y capacidad técnica de los integrantes de los equipos que han diseñado todo esto.

  2. Falta de visión: Lisp tenía y tiene algunas carencias que probablemente influyeron en la decisión de ignorarlo, sin embargo, nadie se imaginó que con solo el 10% de los recursos destinados a hacer cosas nuevas se podrían haber subsanado, en efecto si el 10% de los recursos utilizados en el desarrollo de estupideces en Java, se hubieran invertido en Lisp, hoy sería un lenguaje universal y la informática sería mucho más fácil para todos y para todo.

  3. Mercadeo: es decir propaganda, difusión de prejuicios y stereotipos que pudieron influenciar a los diseñadores para lograr tecnologías adaptadas a los productos de las casas de software, y a los usuarios para que adoptaran estas tecnologías.

Si como pienso, el mercadeo fue la causa fundamental que nos mantiene en este embrollo interminable de lenguajes y herramientas, estaremos condenados por siempre a este purgatorio, donde la propaganda opaca y hasta desaparece cualquier cosa que sea una posible amenaza para las grandes casas de software que no cesan en su intento de monopolizar las tecnologías de información.

Es increíble que como sociedad simplemente aceptemos lo que nos imponen, sin reflexionar sobre las consecuencias, lo mismo nos sucede con Windows (aunque esa es otra historia), y es la confirmación más evidente de la tendencia a la imitación que tenemos los humanos, y de que millones de moscas si pueden estar equivocadas.

Yo por mi parte prefiero ampliamente el filete a la bosta, ¿y ustedes?.


Recursos:
Si están interesados en aprender Lisp, les recomiendo comiencen con Scheme utilizando el libro How to design programs y como ambiente de aprendizaje DrScheme, si son afortunados usuarios de Debian solo deben instalar el paquete drscheme.

9 comentarios:

Dario dijo...

muy buen articulo, bien elaborado y con excelentes referencias que conllevan a un final donde, de manera muy elocuente y ligera, haces ver a Lisp como la mejor opción que necesita mas mercadeo para que los programadores lo conozcan! y vean las ventajas que ofrece en comparacion con las herramientas mas conosidas

rastro dijo...

A desenpolvar mi libro de LISP !!!

Sebastian dijo...

Lindo articulo y llevadero para leer, pero no estoy de acuerdo. Existen varios lenguajes justamente porque cada uno "soluciona" un problema en particular mejor que otro. Si necesito performance extrema, tendre que manejar memoria directamente y recurriré al C o al asm. Nunca lo haria con Python solo. Tampoco podes hacer un sistema operativo en Lisp. Si quiero manejar texto, para que complicarme con C o Java si tengo Perl o Python? Si quiero hacer una GUI para cargar datos, quizas VB sea lo mas rapido y fácil. O sea, toda esta complejidad que existe surgió por necesidad (y un poco por razones comerciales).
Por otra parte, que programa conocido de uso popular se ha hecho en Lisp?

Jose Rey dijo...

Sebastian, gracias por tu comentario, responderé algunas de tus afirmaciones:

Si se puede hacer un sistema operativo en Lisp, solo debes extender Lisp para permitir el manejo de la representación de memoria, el lenguaje BitC (http://www.bitc-lang.org/) fue hecho exactamente para hacer Coyotos igual que C fue hecho para hacer Unix y es Lisp extendido.

Todos los conceptos detrás de VB se pueden aplicar a Lisp, solo hay que cambiar el lenguaje de fondo, eso es lo que hacen Glade y Visual Age for Java.

Lisp puede manejar texto tan bien como Perl, mira Emacs Lisp.

Sobre el programa conocido, estoy totalmente en contra del uso de este argumento, pues es el arma fundamental contra la innovación, y ello apunta al mantenimiento del status quo y particularmente de los grandes monopolios establecidos. De cualquier forma:

Gran parte de Autocad, Emacs y XEmacs están hechos en Lisp, muchos de los programas que verifican la consistencia del diseño de un chip están hechos en Lisp.

kibidango dijo...

Epa José Luis, es fpalm.

No tengo dudas del poder de Lisp. Lo que me parece triste es que descalifiques a Python por el uso de "espacios significativos", eso no es una cuestión "natural" o "contranatura" allí te traiciona la costumbre, hay razones de fondo, más de peso, para promover a Lisp sobre Python, por ejemplo la extensibilidad de Lisp, y bueno, Python también ha avanzado en ese aspecto.

En todo caso, una cosa es cierta, al igual que el dinero, Python no me da la seguridad plena, pero me deja a la vuelta de la esquina, donde está Lisp. Entre los lenguajes más usados en la actualidad Python es el que lo lleva a uno más cerca de Lisp. Y creo que la evolución de Python va, entre otras cosas, a parecerse más y más a Lisp.

Jose Rey dijo...

Los espacios significativos son un gran molestia cuando se hacen herramientas, por ejemplo: los editores de texto no pueden indentar automáticamente con precisión, deben usar heurística porque la indentación da sentido al programa, si piensas en una herramienta que tome pedacitos de programas en python para ensamblar un módulo, tendras dolores de cabeza con la indentación, en cualquier otro lenguaje solo pones los pedacitos entre los delimitadores de cotexto.
paréntesis donde vayan.

Hasta en Haskell, la sintaxis diagramada es una opción que se puede eliminar utilizando llaves para delimitar los contextos léxicos.

Por otra parte en Scheme tienes el SRFI-49 que te da sintaxis diagramada sin perder las expresiones S.

Python tiene ciertamente la ventaja de que su implementación monolítica con una sola implementación del lenguaje permite que todo lo escrito en Python es automáticamente compatible, pero ya no lo es tanto en Jython o IronPython, de cualquier forma nadie los usa por eso mismo, CPython se lleva todo el pote. Pasa lo mismo con Perl y el CPAN.

Aunque Python va hacia Lisp, debería ir tal vez más hacia Scheme, y en efecto tiene problemas importantes con conceptos como las programación funcional, corutinas, etc. entre otras porque a Guido parecen no le gustarle.

Mientras tanto, los Schemers arman un nuevo estándar (R6RS), esperando compatibilizar las librerías de las docenas de versiones de Scheme que existen.

Por qué hay docenas de implementaciones importantes de Scheme/Common Lisp y solo una de Perl, Python y Ruby?

Debe ser por algo, y creo que es la simplicidad de implementación y la versatilidad para orientar Lisp hacia cualquier cosa interesante.

Richzendy dijo...

los usuarios de fedora, tambíén somos afortunados :-D


yum install plt-scheme

brairia dijo...

Hola, mi nombre es Manuel Lorenzo.
Acabo de leer tu artículo sobre Lisp,
y he de decirte que me ha gustado,
quizá porque hace años lo utilicé
más o menos a fondo, aunque siempre
como solución para Autocad.
Creo que eres la persona adecuada
para orientarme en cuanto a su utilización, por lo que te agradecería me respondieses a dos preguntas.
¿Es posible desarrollar aplicaciones para gráficos
independientes de Autocad/Autodesk con este lenguaje?
De ser cierto : ¿Podrías recomendarme por donde empezar ?
! Gracias !

Jose Rey dijo...

Aunque los gráficos no son mi área de interés, creo que una de las cosas para las cuales Lisp es bueno es para el desarrollo de aplicaciones gráficas, ya que ofrece la potencia de la programación funcional, la facilidad de la OO mediante CLOS y hay implementaciones como SBCL que pueden llegar a generar código muy eficicente.

En el cliki tienes un compendio de librerías gráficas