Erlang ( / ˈɜːrlæŋ / UR - lang ) es un lenguaje de programación de alto nivel , funcional , concurrente y de propósito general , y un sistema de ejecución en tiempo de recolección de basura . El término Erlang se usa indistintamente con Erlang/OTP, u Open Telecom Platform (OTP), que consiste en el sistema de ejecución en tiempo de Erlang , varios componentes listos para usar (OTP) escritos principalmente en Erlang, y un conjunto de principios de diseño para programas Erlang. [5]
El sistema de ejecución de Erlang está diseñado para sistemas con estas características:
El lenguaje de programación Erlang tiene datos inmutables , coincidencia de patrones y programación funcional . [7] El subconjunto secuencial del lenguaje Erlang admite evaluación entusiasta , asignación única y tipado dinámico .
Una aplicación Erlang normal se construye a partir de cientos de pequeños procesos Erlang.
Originalmente era un software propietario de Ericsson , desarrollado por Joe Armstrong , Robert Virding y Mike Williams en 1986, [8] pero fue lanzado como software libre y de código abierto en 1998. [9] [10] Erlang/OTP es respaldado y mantenido por la unidad de productos Open Telecom Platform (OTP) en Ericsson .
El nombre Erlang , atribuido a Bjarne Däcker, ha sido asumido por aquellos que trabajaban en las centrales telefónicas (para quienes se diseñó el lenguaje) como una referencia al matemático e ingeniero danés Agner Krarup Erlang y una abreviatura silábica de "Ericsson Language". [8] [11] [12] Erlang fue diseñado con el objetivo de mejorar el desarrollo de aplicaciones de telefonía. [13] La versión inicial de Erlang se implementó en Prolog y fue influenciada por el lenguaje de programación PLEX utilizado en centrales telefónicas anteriores de Ericsson. En 1988 Erlang había demostrado que era adecuado para crear prototipos de centrales telefónicas, pero el intérprete de Prolog era demasiado lento. Un grupo dentro de Ericsson estimó que necesitaría ser 40 veces más rápido para ser adecuado para su uso en producción. En 1992, se comenzó a trabajar en la máquina virtual (VM) BEAM que compila Erlang a C utilizando una mezcla de código compilado de forma nativa y código enhebrado para lograr un equilibrio entre el rendimiento y el espacio en disco. [14] Según el coinventor Joe Armstrong, el lenguaje pasó de ser un producto de laboratorio a aplicaciones reales tras el colapso de la central telefónica AXE de próxima generación , denominada AXE-N, en 1995. Como resultado, se eligió Erlang para la siguiente central telefónica de modo de transferencia asíncrono (ATM), AXD . [8]
En febrero de 1998, Ericsson Radio Systems prohibió el uso interno de Erlang para nuevos productos, citando una preferencia por lenguajes no propietarios. [15] La prohibición provocó que Armstrong y otros hicieran planes para dejar Ericsson. [16] En marzo de 1998, Ericsson anunció el conmutador AXD301, [8] que contenía más de un millón de líneas de Erlang y se informó que lograba una alta disponibilidad de nueve "9" . [17] En diciembre de 1998, la implementación de Erlang se convirtió en código abierto y la mayoría del equipo de Erlang renunció para formar una nueva empresa, Bluetail AB. [8] Ericsson finalmente relajó la prohibición y volvió a contratar a Armstrong en 2004. [16]
En 2006, se agregó soporte nativo de multiprocesamiento simétrico al sistema de tiempo de ejecución y a la máquina virtual. [8]
Las aplicaciones Erlang se construyen a partir de procesos Erlang muy ligeros en el sistema de ejecución de Erlang. Los procesos Erlang pueden considerarse objetos "vivos" ( programación orientada a objetos ), con encapsulación de datos y paso de mensajes , pero capaces de cambiar su comportamiento durante el tiempo de ejecución. El sistema de ejecución de Erlang proporciona un estricto aislamiento de procesos entre los procesos Erlang (esto incluye la recolección de datos y basura, separados individualmente por cada proceso Erlang) y una comunicación transparente entre procesos (consulte Transparencia de ubicación ) en diferentes nodos Erlang (en diferentes hosts).
Joe Armstrong, coinventor de Erlang, resumió los principios de los procesos en su tesis doctoral : [18]
Joe Armstrong comentó en una entrevista con Rackspace en 2013: "Si Java es ' escribir una vez, ejecutar en cualquier lugar ', entonces Erlang es 'escribir una vez, ejecutar para siempre'". [19]
En 2014, Ericsson informó que Erlang se estaba utilizando en sus nodos de soporte y en redes móviles GPRS , 3G y LTE en todo el mundo y también por Nortel y Deutsche Telekom . [20]
Erlang se utiliza en RabbitMQ . Como expresó Tim Bray , director de Tecnologías Web en Sun Microsystems , en su discurso inaugural en la Convención de Código Abierto O'Reilly (OSCON) en julio de 2008:
Si alguien viniera a mí y quisiera pagarme mucho dinero para construir un sistema de manejo de mensajes a gran escala que realmente tuviera que estar activo todo el tiempo y que nunca pudiera permitirse el lujo de caerse durante años, sin dudarlo elegiría Erlang para construirlo.
Erlang es el lenguaje de programación utilizado para codificar WhatsApp . [21]
También es el lenguaje elegido por Ejabberd , un servidor de mensajería XMPP .
Elixir es un lenguaje de programación que se compila en código de bytes BEAM (a través del formato abstracto Erlang). [22]
Desde que se lanzó como código abierto, Erlang se ha extendido más allá de las telecomunicaciones, estableciéndose en otros mercados verticales como FinTech, juegos, atención médica, automoción, Internet de las cosas y blockchain. Además de WhatsApp, hay otras empresas que figuran como historias de éxito de Erlang: Vocalink (una empresa de MasterCard), Goldman Sachs , Nintendo , AdRoll, Grindr , BT Mobile , Samsung , OpenX y SITA . [23] [24]
Un algoritmo factorial implementado en Erlang:
- módulo ( fact ). % Este es el archivo 'fact.erl', el módulo y el nombre del archivo deben coincidir - exportación ([ fac / 1 ]). % Esto exporta la función 'fac' de aridad 1 (1 parámetro, sin tipo, sin nombre) fac ( 0 ) -> 1 ; % Si es 0, entonces devuelve 1, de lo contrario (note el punto y coma ; que significa 'else') fac ( N ) cuando N > 0 , is_integer ( N ) -> N * fac ( N - 1 ). % Determinar recursivamente, luego devolver el resultado % (note el punto . que significa 'endif' o 'fin de la función') %% Esta función se bloqueará si se proporciona cualquier cosa que no sea un entero no negativo. %% Ilustra la filosofía "Dejar que se bloquee" de Erlang.
Un algoritmo recursivo de cola que produce la secuencia de Fibonacci :
%% La declaración del módulo debe coincidir con el nombre del archivo "series.erl" - módulo ( series ).%% La declaración de exportación contiene una lista de todas las funciones que forman %% la API pública del módulo. En este caso, este módulo expone una única función %% llamada fib que toma 1 argumento (es decir, tiene una aridad de 1) %% La sintaxis general para -export es una lista que contiene el nombre y la aridad de cada función pública - export ([ fib / 1 ]).%% --------------------------------------------------------------------- %% API pública %% ---------------------------------------------------------------------%% Manejar casos en los que fib/1 recibe valores específicos %% El orden en el que se declaran estas firmas de función es una parte vital %% de la funcionalidad de este módulo%% Si fib/1 recibe un número negativo, entonces devuelve el átomo err_neg_val %% Normalmente, este tipo de codificación defensiva no se recomienda debido a la filosofía de Erlang 'Deja que %% se bloquee', pero aquí el resultado sería un bucle infinito. fib ( N ) cuando N < 0 -> err_neg_val ; %% Si a fib/1 se le pasa exactamente el entero 0, entonces devuelve 0 fib ( 0 ) -> 0 ; %% Para todos los demás valores, llame a la función privada fib_int/3 para realizar %% el cálculo fib ( N ) -> fib_int ( N - 1 , 0 , 1 ). %% --------------------------------------------------------------------- %% API privada %% ---------------------------------------------------------------------%% Si fib_int/3 recibe 0 como su primer argumento, entonces hemos terminado, por lo que %% devuelve el valor en el argumento B. El segundo argumento se denota _ a %% para ignorar su valor. fib_int ( 0 , _, B ) -> B ; %% Para todas las demás combinaciones de argumentos, llame recursivamente a fib_int/3 %% donde cada llamada hace lo siguiente: %% - decrementar el contador N %% - pasar el tercer argumento como el nuevo segundo argumento %% - pasar la suma del segundo y tercer argumento como el nuevo %% tercer argumento fib_int ( N , A , B ) -> fib_int ( N - 1 , B , A + B ).
Omitir los comentarios da como resultado un programa mucho más corto.
- módulo ( serie ). - exportación ([ fib / 1 ]).fib ( N ) cuando N < 0 -> err_neg_val ; fib ( 0 ) -> 0 ; fib ( N ) -> fib_int ( N - 1 , 0 , 1 ). fib_int ( 0 , _, B ) -> B ; fib_int ( N , A , B ) -> fib_int ( N - 1 , B , A + B ).
Ordenación rápida en Erlang, utilizando la comprensión de listas : [25]
%% qsort:qsort(List) %% Ordena una lista de elementos - módulo ( qsort ). % Este es el archivo 'qsort.erl' - export ([ qsort / 1 ]). % Se exporta una función 'qsort' con 1 parámetro (sin tipo, sin nombre) qsort ([]) -> []; % Si la lista [] está vacía, devuelve una lista vacía (nada que ordenar) qsort ([ Pivot | Rest ]) -> % Componga recursivamente una lista con 'Front' para todos los elementos que deben estar antes de 'Pivot' % luego 'Pivot' luego 'Back' para todos los elementos que deben estar después de 'Pivot' qsort ([ Front || Front <- Rest , Front < Pivot ]) ++ [ Pivot ] ++ qsort ([ Back || Back <- Rest , Back >= Pivot ]).
El ejemplo anterior invoca recursivamente la función qsort
hasta que no quede nada por ordenar. La expresión [Front || Front <- Rest, Front < Pivot]
es una comprensión de lista , lo que significa "Construir una lista de elementos Front
tal que Front
sea miembro de Rest
y Front
sea menor que Pivot
". ++
es el operador de concatenación de listas.
Se puede utilizar una función de comparación para estructuras más complicadas con el fin de facilitar la lectura.
El siguiente código ordenaría las listas según su longitud:
% Este es el archivo 'listsort.erl' (el compilador está hecho de esta manera) - módulo ( listsort ). % Exportar 'by_length' con 1 parámetro (no importa el tipo y el nombre) - exportar ([ by_length / 1 ]).by_length ( Listas ) -> % Utilice 'qsort/2' y proporcione una función anónima como parámetro qsort ( Listas , fun ( A , B ) -> length ( A ) < length ( B ) end ). qsort ([], _) -> []; % Si la lista está vacía, devuelve una lista vacía (ignora el segundo parámetro) qsort ([ Pivot | Rest ], Smaller ) -> % Particiona la lista con elementos 'Smaller' delante de 'Pivot' y elementos que no sean 'Smaller' % después de 'Pivot' y ordena las sublistas. qsort ([ X || X <- Rest , Smaller ( X , Pivot )], Smaller ) ++ [ Pivot ] ++ qsort ([ Y || Y <- Rest , no ( Smaller ( Y , Pivot ))], Smaller ).
A Pivot
se toma del primer parámetro dado a qsort()
y el resto de Lists
se llama Rest
. Tenga en cuenta que la expresión
[ X || X <- Resto , Menor ( X , Pivote )]
No es diferente en forma de
[ Frente || Frente <- Descanso , Frente < Pivote ]
(en el ejemplo anterior) excepto por el uso de una función de comparación en la última parte, que dice "Construye una lista de elementos X
tal que X
sea miembro de Rest
, y Smaller
sea verdadera", Smaller
siendo definida anteriormente como
diversión ( A , B ) -> longitud ( A ) < longitud ( B ) fin
La función anónima se nombra Smaller
en la lista de parámetros de la segunda definición de qsort
para que se pueda hacer referencia a ella por ese nombre dentro de esa función. No se nombra en la primera definición de qsort
, que trata con el caso base de una lista vacía y, por lo tanto, no necesita esta función, y mucho menos un nombre para ella.
Erlang tiene ocho tipos de datos primitivos :
make_ref()
.spawn(...)
Los Pid son referencias a procesos Erlang.open_port
. Se pueden enviar y recibir mensajes desde los puertos, pero estos mensajes deben obedecer al llamado "protocolo de puerto".fun(...) -> ... end
.Y tres tipos de datos compuestos:
{D1,D2,...,Dn}
denota una tupla cuyos argumentos son D1, D2, ... Dn.
Los argumentos pueden ser tipos de datos primitivos o tipos de datos compuestos. Se puede acceder a cualquier elemento de una tupla en tiempo constante.[Dh|Dt]
denota una lista cuyo primer elemento es Dh
, y cuyos elementos restantes son la lista Dt
. La sintaxis []
denota una lista vacía. La sintaxis [D1,D2,..,Dn]
es la abreviatura de [D1|[D2|..|[Dn|[]]]]
. Se puede acceder al primer elemento de una lista en tiempo constante. El primer elemento de una lista se denomina cabecera de la lista. El resto de una lista cuando se ha eliminado la cabecera se denomina cola de la lista.#{Key1=>Value1,...,KeyN=>ValueN}
.Se proporcionan dos formas de azúcar sintáctico :
[99,97,116]
. [26]Erlang no tiene ningún método para definir clases, aunque hay bibliotecas externas disponibles. [27]
Erlang está diseñado con un mecanismo que facilita que los procesos externos monitoreen las fallas (o los fallos de hardware), en lugar de un mecanismo en proceso como el manejo de excepciones utilizado en muchos otros lenguajes de programación. Las fallas se informan como otros mensajes, que es la única forma en que los procesos pueden comunicarse entre sí, [28] y los subprocesos se pueden generar de forma económica (ver más abajo). La filosofía de "dejar que se bloquee" prefiere que un proceso se reinicie por completo en lugar de intentar recuperarse de una falla grave. [29] Aunque todavía requiere el manejo de errores, esta filosofía da como resultado menos código dedicado a la programación defensiva donde el código de manejo de errores es altamente contextual y específico. [28]
Una aplicación típica de Erlang se escribe en forma de árbol de supervisores. Esta arquitectura se basa en una jerarquía de procesos en la que el proceso de nivel superior se conoce como "supervisor". El supervisor genera varios procesos secundarios que actúan como trabajadores o como supervisores de nivel inferior. Estas jerarquías pueden existir hasta niveles arbitrarios y han demostrado proporcionar un entorno altamente escalable y tolerante a fallos dentro del cual se puede implementar la funcionalidad de la aplicación.
Dentro de un árbol de supervisores, todos los procesos supervisores son responsables de gestionar el ciclo de vida de sus procesos secundarios, y esto incluye gestionar situaciones en las que esos procesos secundarios fallan. Cualquier proceso puede convertirse en supervisor generando primero un proceso secundario y luego invocando erlang:monitor/2
ese proceso. Si el proceso supervisado luego falla, el supervisor recibirá un mensaje que contiene una tupla cuyo primer miembro es el átomo 'DOWN'
. El supervisor es responsable, en primer lugar, de escuchar dichos mensajes y, en segundo lugar, de tomar la acción adecuada para corregir la condición de error.
La principal fortaleza de Erlang es el soporte para concurrencia . Tiene un pequeño pero poderoso conjunto de primitivas para crear procesos y comunicarse entre ellos. Erlang es conceptualmente similar al lenguaje occam , aunque reformula las ideas de comunicación de procesos secuenciales (CSP) en un marco funcional y utiliza el paso de mensajes asincrónico. [30] Los procesos son el medio principal para estructurar una aplicación Erlang. No son procesos del sistema operativo ni hilos , sino procesos livianos que son programados por BEAM. Al igual que los procesos del sistema operativo (pero a diferencia de los hilos del sistema operativo), no comparten estado entre sí. La sobrecarga mínima estimada para cada uno es de 300 palabras . [31] Por lo tanto, se pueden crear muchos procesos sin degradar el rendimiento. En 2005, se realizó con éxito un benchmark con 20 millones de procesos con Erlang de 64 bits en una máquina con 16 GB de memoria de acceso aleatorio (RAM; total 800 bytes/proceso). [32] Erlang ha soportado multiprocesamiento simétrico desde la versión R11B de mayo de 2006.
Si bien los subprocesos necesitan soporte de bibliotecas externas en la mayoría de los lenguajes, Erlang proporciona funciones a nivel de lenguaje para crear y administrar procesos con el objetivo de simplificar la programación concurrente. Aunque toda la concurrencia es explícita en Erlang, los procesos se comunican mediante el paso de mensajes en lugar de variables compartidas, lo que elimina la necesidad de bloqueos explícitos (la máquina virtual aún utiliza un esquema de bloqueo internamente). [33]
La comunicación entre procesos funciona a través de un sistema de paso de mensajes asincrónico sin compartir nada : cada proceso tiene un "buzón", una cola de mensajes que han sido enviados por otros procesos y que aún no se han consumido. Un proceso utiliza el primitivo para recuperar los mensajes que coinciden con los patrones deseados. Una rutina de manejo de mensajes prueba los mensajes uno por uno con cada patrón, hasta que uno de ellos coincide. Cuando el mensaje se consume y se elimina del buzón, el proceso reanuda la ejecución. Un mensaje puede incluir cualquier estructura de Erlang, incluidas las primitivas (enteros, flotantes, caracteres, átomos), tuplas, listas y funciones. receive
El siguiente ejemplo de código muestra el soporte integrado para procesos distribuidos:
% Crea un proceso e invoca la función web:start_server(Port, MaxConnections) ServerProcess = spawn ( web , start_server , [ Port , MaxConnections ]), % Crea un proceso remoto e invoca la función % web:start_server(Port, MaxConnections) en la máquina RemoteNode RemoteProcess = spawn ( RemoteNode , web , start_server , [ Port , MaxConnections ]), % Envía un mensaje a ServerProcess (de forma asíncrona). El mensaje consta de una tupla % con el átomo "pausa" y el número "10". ServerProcess ! { pausa , 10 }, % Recibir mensajes enviados a este proceso recibir un_mensaje -> hacer_algo ; { datos , ContenidoDeDatos } -> handle ( ContenidoDeDatos ); { hola , Texto } -> io : formato ( "Recibí mensaje de saludo: ~s " , [ Texto ]); { adiós , Texto } -> io : formato ( "Recibí mensaje de despedida: ~s " , [ Texto ]) fin .
Como muestra el ejemplo, se pueden crear procesos en nodos remotos y la comunicación con ellos es transparente en el sentido de que la comunicación con procesos remotos funciona exactamente como la comunicación con procesos locales.
La concurrencia respalda el método principal de manejo de errores en Erlang. Cuando un proceso falla, sale sin problemas y envía un mensaje al proceso que lo controla, que luego puede tomar medidas, como iniciar un nuevo proceso que se haga cargo de la tarea del proceso anterior. [34] [35]
La implementación de referencia oficial de Erlang utiliza BEAM . [36] BEAM está incluido en la distribución oficial de Erlang, llamada Erlang/OTP. BEAM ejecuta bytecode que se convierte a código enhebrado en el momento de la carga. También incluye un compilador de código nativo en la mayoría de las plataformas, desarrollado por el Proyecto High Performance Erlang (HiPE) en la Universidad de Uppsala . Desde octubre de 2001, el sistema HiPE está completamente integrado en el sistema de código abierto Erlang/OTP de Ericsson. [37] También admite la interpretación, directamente desde el código fuente a través del árbol de sintaxis abstracta , mediante script a partir de la versión R11B-5 de Erlang.
Erlang admite la actualización dinámica de software a nivel de lenguaje . Para implementar esto, el código se carga y se administra como unidades de "módulo"; el módulo es una unidad de compilación . El sistema puede mantener dos versiones de un módulo en la memoria al mismo tiempo, y los procesos pueden ejecutar código de cada una de ellas simultáneamente. Las versiones se denominan versión "nueva" y versión "antigua". Un proceso no pasará a la nueva versión hasta que realice una llamada externa a su módulo.
Un ejemplo del mecanismo de carga de código activo:
%% Un proceso cuyo único trabajo es mantener un contador. %% Primera versión - módulo ( contador ). - exportación ([ inicio / 0 , codeswitch / 1 ]). inicio () -> bucle ( 0 ). loop ( Suma ) -> recibir { incremento , Contar } -> loop ( Suma + Contar ); { contador , Pid } -> Pid ! { contador , Suma }, loop ( Suma ); code_switch -> ? MÓDULO : codeswitch ( Suma ) % Fuerza el uso de 'codeswitch/1' desde la última versión del MÓDULO fin . codeswitch ( Suma ) -> bucle ( Suma ).
Para la segunda versión, agregamos la posibilidad de restablecer el conteo a cero.
%% Segunda versión - módulo ( contador ). - exportación ([ inicio / 0 , codeswitch / 1 ]). inicio () -> bucle ( 0 ). loop ( Suma ) -> recibir { incremento , Contar } -> loop ( Suma + Contar ); reiniciar -> loop ( 0 ); { contador , Pid } -> Pid ! { contador , Suma }, loop ( Suma ); code_switch -> ? MODULE : codeswitch ( Suma ) fin . codeswitch ( Suma ) -> bucle ( Suma ).
Solo cuando se recibe un mensaje que consiste en el átomo, code_switch
el bucle ejecutará una llamada externa a codeswitch/1 ( ?MODULE
es una macro de preprocesador para el módulo actual). Si hay una nueva versión del módulo contador en la memoria, se llamará a su función codeswitch/1. La práctica de tener un punto de entrada específico a una nueva versión permite al programador transformar el estado a lo que se necesita en la versión más nueva. En el ejemplo, el estado se mantiene como un entero.
En la práctica, los sistemas se construyen utilizando principios de diseño de la Plataforma de telecomunicaciones abiertas, lo que permite obtener diseños con mayor capacidad de actualización de código. La carga de código activo exitosa es exigente. El código debe escribirse con cuidado para aprovechar las funciones de Erlang.
En 1998, Ericsson lanzó Erlang como software libre y de código abierto para garantizar su independencia de un único proveedor y aumentar la conciencia sobre el lenguaje. Erlang, junto con las bibliotecas y la base de datos distribuida en tiempo real Mnesia , forma la colección de bibliotecas OTP. Ericsson y algunas otras empresas ofrecen soporte comercial a Erlang.
Desde su lanzamiento como código abierto, Erlang ha sido utilizado por varias empresas en todo el mundo, incluidas Nortel y Deutsche Telekom . [38] Aunque Erlang fue diseñado para llenar un nicho y ha permanecido como un lenguaje oscuro durante la mayor parte de su existencia, su popularidad está creciendo debido a la demanda de servicios concurrentes. [39] [40] Erlang ha encontrado algún uso en el campo de servidores de juegos de rol multijugador masivos en línea (MMORPG). [41]
{{cite book}}
: |journal=
ignorado ( ayuda )Erlang es conceptualmente similar al lenguaje de programación Occam, aunque reformula las ideas de CSP en un marco funcional y utiliza el paso de mensajes asincrónico.
El mayor usuario de Erlang es (¡sorpresa!) Ericsson. Ericsson lo utiliza para escribir software utilizado en sistemas de telecomunicaciones. Muchas docenas de proyectos lo han utilizado, uno particularmente grande es el conmutador ATM AXD301 extremadamente escalable.
Otros usuarios comerciales enumerados como parte de las preguntas frecuentes incluyen: Nortel, Deutsche Flugsicherung (la organización nacional alemana
de control del tráfico aéreo
) y T-Mobile.
Prácticamente todos los lenguajes utilizan concurrencia de estado compartido. Esto es muy difícil y conduce a problemas terribles cuando se gestionan los fallos y se amplía el sistema... Algunas empresas emergentes con un ritmo bastante rápido en el mundo financiero se han sumado a Erlang; por ejemplo, la sueca www.kreditor.se.
Les resultará fácil añadir características del lenguaje para que sean como Erlang. Les llevará mucho tiempo construir una máquina virtual de tan alta calidad y bibliotecas maduras para la concurrencia y la fiabilidad. Por tanto, Erlang está preparado para el éxito. Si quieres construir una aplicación multinúcleo en los próximos años, deberías echarle un vistazo a Erlang.