Erlang, ¿realmente lo entienden?

Después de leer varios artículos sobre Erlang, algunos defendiéndolo y otros criticándolo, me doy cuenta de que, realmente, no todo el mundo entiende lo que es y significa este lenguaje. En sí, no es el lenguaje, sino la metodología que hay detrás la que está moviendo el hecho de que los lenguajes funcionales sean como son y sirvan, de la forma correcta, a los propósitos para los que fueron creados.

Criticando su estilo

Es curioso que gente como Damien Katz (creador de CouchDB), o Toni Arcieri (creador de Reia) hayan sido de los primeros (o al menos los que he encontrado) que hayan arrojado una piedra en contra del lenguaje que eligieron para desarrollar elementos que han sido bastante respetados, apoyados y usados.

En este aspecto, otros como Yariv Sadan, han defendido y reconocido algunos de estos comentarios y, tras leer a unos y a otros, me queda la pregunta de, ¿realmente han entendido para qué sirve Erlang?, ¿qué hace y cuales son sus potencias?, ¿cómo sacarle el máximo provecho así como programar orientados a la concurrencia?

Crítica a la sintaxis

Lo primero que critican de Erlang es su sintaxis. Recuerdo la primera vez que vi el lenguaje C, después de haber programado en Basic, Pascal y Modula–2 que dije… ¡qué raro! y ya ni digamos de cuando unos años más tarde comencé a ver Perl… :-D

La sintaxis C-like, como dicen muchos, es muy extendida, pero no es nada comprensible para una persona que comienza desde cero. Niklaus Wirth desarrollo por esto mismo lenguajes como Pascal, para que sirviesen de aprendizaje al modo pseudocódigo. Un código que nada más leerlo, se entienda.

Erlang, en cambio, sigue una metodología más como Prolog, está orientado al desarrollo de funciones matemáticas, por ello, la función tiene una conclusión (->) para su comienzo y termina con un punto final. Cada elemento dentro de la función o expresión matemática se separa por comas, y todo es funcional, es decir, todo elemento en Erlang hace algo y DEBE retornar algo, incluso las estructuras condicionales, por eso tiene la forma que tiene.

Tipos de datos: registros y cadenas de caracteres

Los registros, son a nivel de preprocesador: tuplas. Esto quiere decir que funciones del tipo record_info no existen realmente, por lo que no se pueden emplear variables para pasarle a esa macro y queda el espectro del uso de los registros algo limitado. No obstante, hay algunos trucos que se pueden emplear y existen para paliar esto, en caso de que se requiera/necesite.

Otro de los problemas, con los tipos de datos, son las cadenas de caracteres, las cuales son listas de enteros. Esto no debe de extrañarnos, ya que en todos los lenguajes son listas (o arrays, o vectores) de bytes. En lenguaje C, por ejemplo:

char c[] = "Esto es una cadena";
char d[] = { 'E', 's', 't', 'o', 32, 'e', 's', 32, ...,  };
 
printf("%s\n", strcmp(c, d)== ? "true" : "false");

Esto nos dará verdadero, ya que ambas son listas de bytes, al igual que en Erlang podemos ver algo parecido con las listas de enteros:

[100,111,103] == "dog".

Lo cual también retornará verdadero.

Rendimiento: Churras con Merinas

Realmente, al leer el artículo de Toni, cuando llego al punto que dice: Erlang sucks at managing memory; habla de una comparación entre Erlang y Azul Vega, un procesador de 54 cores optimizado para ejecutar la JVM.

Un programa con ese procesador y esa JVM, NO se ejecutaría de forma distribuida, realmente, ya que solo se distribuyen las tareas posibles e internas de la JVM entre los cores, en tanto y en cuanto se puedan distribuir estas tareas. No obstante, no está diseñado para conectarse con otros nodos, por lo que NO es escalable. Además, solo tiene cuatro canales para acceso a memoria, por lo que, 27 máquinas de Pentium IV con HyperThreading podrían hacer ese mismo trabajo con Erlang… y aún así aún podríamos poner más hierro a bajo coste para seguir escalando y mejorando el rendimiento del software desarrollado.

¿Es un lenguaje de propósito general?

Erlang fue creado para entornos en los que hubiese que desarrollar software con alto grado de concurrencia, esto lo hace ideal para entornos servidores de cualquier tipo, y actualmente, para los escritorios modernos, donde los programas se ejecutan de forma concurrente, claro.

¿Elrang puede desarrollar web? Sí, ejemplos como ChicagoBoss, o Erlyweb, o Nitrogen lo atestiguan. ¿Erlang puede desarrollar aplicaciones de escritorio? Sí, ejemplos como Wings3D dan crédito de ello. ¿Erlang sirve para desarrollar aplicaciones de servidor? ¡Claro!, para eso fue creado.

En mi opinión, si PHP tiene esa denominación y es un lenguaje de marcas, Erlang DEBE de tenerla :-D

Asignación única… ¿por qué?

Para trazar mejor los códigos, claro, y porque bien pensado, un lenguaje funcional no requiere del uso de tantas variables, por ejemplo, tal y como decía Damien Katz en su artículo, tienes este código:

f(X) ->
  X1 = foo(X),
  X2 = bar(X1),
  baz(X2).

Este código, bien escrito, sería:

f(X) ->
  baz(bar(foo(X))).

¿Quieres agregar logs para que se vean en el cambio de funciones y no los quieres poner dentro de las propias funciones, sino entre las llamadas… pues puedes hacerlo con otras funciones:

log(Caller, X) -> io:format("~s: ~p~n", [Caller, X]), X.
f(X) ->
  log("BAZ: ", baz(log("BAR: ", bar(log("FOO: ", foo(X)))))).

Realmente no es complejo si se escribe y ordena el código de forma adecuada… en otros lenguajes, como por ejemplo C, se pueden escribir cosas como esta:

char *s = "Parsea la cadena";
int size;
for (int i=, size; s[i]!='\0' && i<100; i++, size++)
  ;

Otros códigos en Perl dan incluso hasta más miedo… pero es como siempre se dice… depende del programador.

El desarrollo en sí

Lo que creo que no se entiende, es que el desarrollo en Erlang, como tal, no es ni escalable, ni redundante, ni aprovecha todos los núcleos y procesadores de las máquinas, así como el resto de núcleos que estén conectados. El hecho de que se pueda aprovechar la forma en la que Erlang fue diseñado, es desarrollar el software de forma concurrente. Con los patrones adecuados.

Por ejemplo, Erlang puede, en un solo nodo, ejecutar casi 2 millones de procesos concurrentes. Esto quiere decir que, cada tarea que haya que realizar, debería de correr en su propio proceso, y este proceso es el que se lanza en el núcleo, procesador y nodo que corresponda. Como cuando se desarrollaba en PVM o MPICH.

Emplear los patrones de OTP es esencial para sacar el máximo provecho de la arquitectura de Erlang, y el crear manejadores de eventos, máquinas de estos, memorias compartidas a través de servidores, etc. debe de realizarse, necesariamente a través de los mecanismos que aporta Erlang.

Si se emplease Erlang para hacer llamadas a Mnesia, desde cualquier parte del código con las llamadas dirty y generando un nuevo proceso por cada petición aislada, tendríamos un programa que puede tiene potenciales errores de concurrencia, y que no aprovecha los nodos que tenga configurados y conectados el sistema.

Conclusión

Me parece muy poco profesional escribir este tipo de artículos, tanto para Tony como para Damien, en los que ponen una serie de circunstancias poco contrastadas y debidas al bajo conocimiento del lenguaje y, precisamente ellos, que han desarrollado grandes herramientas en este lenguaje.

Java, igualmente, es un gran lenguaje, pero como el mismo Tony comenta, requiere de que la JVM cumpla la especificación RTSJ y, en las comparaciones, por ejemplo, hablan incluso de que habría que adquirir un hardware especial para poder desarrollar algo parecido a lo que Erlang hace en cualquier arquitectura, creo que son muy poco objetivos.