Primeros pasos con el lenguaje Erlang

Hola Mundo

Hay dos cosas que deberá saber al escribir una aplicación “hola mundo” en Erlang:

  1. El código fuente está escrito en lenguaje de programación erlang utilizando el editor de texto de su elección
  2. A continuación, la aplicación se ejecuta en la máquina virtual erlang. En este ejemplo, interactuaremos con la máquina virtual de erlang a través del shell de erlang.

Primero el código fuente de la aplicación:

Cree un nuevo archivo hello.erl que contenga lo siguiente:

-module(hello).
-export([hello_world/0]).

hello_world() ->
  io:format("Hello, World!~n", []).

Echemos un vistazo rápido a lo que esto significa:

  • -módulo (hola). Todas las funciones erlang existen dentro de un módulo. Luego, los módulos se utilizan para crear aplicaciones, que son una colección de módulos. Esta primera línea es para identificar este módulo, es decir, hola. Los módulos se pueden comparar con los paquetes de Java
  • -export([hello_world/0]). Le dice al compilador qué funciones hacer “públicas” (en comparación con los lenguajes OO), y la aridad de la función relevante. La aridad es el número de argumentos que toma la función. Ya que en erlang una función con 1 argumento se ve como una función diferente a una con 2 argumentos aunque el nombre sea exactamente el mismo. Es decir, hola_mundo/0 es una función completamente diferente a hola_mundo/1, por ejemplo.
  • hello_world() Este es el nombre de la función. El -> indica la transición a la implementación (cuerpo) de la función. Esto se puede leer como “hello_world() se define como…”. Tenga en cuenta que hello_world() (sin argumentos) se identifica con hello_world/0 en la máquina virtual y hello_world(Some_Arg) como hello_world/1.
  • io:format("Hello, World!~n", []) Desde el módulo io, se llama a la función format/2, que es la función para la salida estándar. ~n es un especificador de formato que significa imprimir una nueva línea. El [] es una lista de variables para imprimir indicadas por los especificadores de formato en la cadena de salida, que en este caso es nada.
  • Todas las declaraciones de erlang deben terminar con un . (punto).

En Erlang, se devuelve el resultado de la última declaración en una función.

Ahora, ejecutemos nuestra aplicación:

Inicie el shell erlang desde el mismo directorio que el archivo hello.erl:

$ eh

Debería obtener un mensaje similar a este (su versión puede ser diferente):

Eshell V8.0  (abort with ^G)
1>

Ahora ingrese los siguientes comandos:

1> c(hello).
{ok,hello}
2> hello:hello_world().
Hello, World!
ok

Repasemos cada línea una por una:

  • c(hola) - este comando llama a la función c en un átomo hola. Esto le dice a Erlang que busque el archivo hello.erl, lo compile en un módulo (se generará un archivo llamado hello.beam en el directorio) y lo cargue en el entorno.
  • {ok, hola}: este es el resultado de llamar a la función c anterior. Es una tupla que contiene un átomo ok y un átomo hola. Las funciones de Erlang generalmente devuelven {ok, Something} o {error, Reason}.
  • hello:hello_world() - llama a la función hello_world() desde el módulo hello.
  • ¡Hola, mundo!: esto es lo que imprime nuestra función.
  • ok - esto es lo que devolvió nuestra función. Dado que Erlang es un lenguaje de programación funcional, cada función devuelve algo. En nuestro caso, aunque no devolvimos nada en hello_world(), la última llamada en esa función fue a io:format(...) y esa función devolvió ok, que es a su vez lo que nuestra función regresó.

Comprensión de listas

Las comprensiones de lista son una construcción sintáctica para crear una lista basada en listas existentes. En erlang, la comprensión de una lista tiene la forma [Expr || Calificador1, ..., CalificadorN]. Donde los calificadores son generadores Pattern <- ListExpr o filtros como integer(X) que evalúan como true o false.

El siguiente ejemplo muestra una lista de comprensión con un generador y dos filtros.

[X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3].

El resultado es una lista que contiene solo números enteros mayores que 3.

[4,5,6]

Módulos

Un módulo erlang es un archivo con un par de funciones agrupadas. Este archivo suele tener la extensión .erl.

A continuación se muestra un módulo “Hello World” con el nombre hello.erl

-module(hello).
-export([hello_world/0]).

hello_world() ->
  io:format("Hello, World!~n", []).

En el archivo, se requiere declarar el nombre del módulo. Como se muestra antes en la línea 1. El nombre del módulo y el nombre del archivo antes de la extensión .erl deben ser iguales.

Función

La función es un conjunto de instrucciones, que se agrupan. Estas instrucciones agrupadas juntas realizan ciertas tareas. En erlang, todas las funciones devolverán un valor cuando se llamen.

A continuación se muestra un ejemplo de una función que suma dos números

add(X, Y)-> X + Y.

Esta función realiza una operación de suma con valores X e Y y devuelve el resultado. La función se puede utilizar de la siguiente manera

add(2,5).

Las declaraciones de funciones pueden constar de varias cláusulas, separadas por un punto y coma. Los Argumentos en cada una de estas cláusulas se evalúan mediante coincidencia de patrones. La siguiente función devolverá ’tupla’ si el Argumento es una tupla en la Forma: {prueba, X} donde X puede ser cualquier valor. Devolverá ’lista’, si el Argumento es una lista de longitud 2 en la forma [“prueba”, X], y devolverá ‘{error, “Razón”}’ en cualquier otro caso:

function({test, X}) -> tuple;
function(["test", X]) -> list;
function(_) -> {error, "Reason"}.

Si el argumento no es una tupla, se evaluará la segunda cláusula. Si el argumento no es una lista, se evaluará la tercera cláusula.

Las declaraciones de funciones pueden consistir en los llamados ‘guardias’ o ‘secuencias de guardas’. Estas Guardias son expresiones que limitan la evaluación de una función. Una función con Guardias solo se ejecuta cuando todas las Expresiones de Guardia dan un valor verdadero. Varios guardias se pueden separar con un punto y coma.

function_name(Argument) when Guard1; Guard2; ... GuardN -> (...).

La función ’nombre_función’ solo se evaluará cuando la secuencia de guardia sea verdadera. La siguiente función devolverá verdadero solo si el argumento X está en el rango adecuado (0..15):

in_range(X) when X>=0; X<16 -> true;
in_range(_) -> false.

Iniciar y detener el Erlang Shell

Inicio del shell Erlang

En un sistema UNIX, inicia el shell Erlang desde un símbolo del sistema con el comando erl

Ejemplo:

$ erl
Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V7.0  (abort with ^G)
1> 

El texto que se muestra cuando inicia el shell le brinda información sobre qué versión de Erlang está ejecutando, así como otra información útil sobre el sistema erlang.

Para iniciar el shell en Windows, haga clic en el icono de Erlang en el menú de inicio de Windows.

Deteniendo el caparazón de Erlang

Para una salida controlada del shell erlang, escriba:

Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V7.0  (abort with ^G)
1> q().

También puede salir del shell de Erlang presionando Ctrl+C en sistemas UNIX o Ctrl+Break en Windows, lo que lo lleva al siguiente mensaje:

Erlang/OTP 18 [erts-7.0] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V7.0  (abort with ^G)
1> 
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution

Si luego presiona a (para abortar), saldrá directamente del shell.

Otras formas de salir del shell erlang son: init:stop(), que hace lo mismo que q() o erlang:halt().

La coincidencia de patrones

Una de las operaciones más comunes en erlang es la coincidencia de patrones. Se usa cuando se asigna un valor a una variable, en declaraciones de funciones y en estructuras de flujo de control como declaraciones case y receive. Una operación de coincidencia de patrones necesita al menos 2 partes: un patrón y un término con el que se compara el patrón.

Una asignación de variable en erlang se ve así:

X = 2.

En la mayoría de los lenguajes de programación, la semántica de esta operación es sencilla: vincula un valor (2) a un nombre de tu elección (la variable, en este caso, X). Erlang tiene un enfoque ligeramente diferente: haga coincidir el patrón del lado izquierdo (X) con el término del lado derecho (2). En este caso, el efecto es el mismo: la variable X ahora está vinculada al valor 2. Sin embargo, con la coincidencia de patrones puede realizar tareas más estructuradas.

{Type, Meta, Doc} = {document, {author, "Alice"}, {text, "Lorem Ipsum"}}.

Esta operación de emparejamiento se realiza analizando la estructura del término del lado derecho y aplicando todas las variables del lado izquierdo a los valores apropiados del término, de modo que el lado izquierdo sea igual al lado derecho. En este ejemplo, Tipo está vinculado al término: documento, Meta a {autor, "Alice"} y Doc a {texto, "Lorem Ipsum"}. En este ejemplo en particular, se supone que las variables: Type, Meta y Doc están sin vincular, por lo que se puede usar cada variable.

También se pueden crear coincidencias de patrones utilizando variables vinculadas.

Identifier = error.

La variable Identificador ahora está vinculada al valor error. La siguiente operación de coincidencia de patrones funciona, porque la estructura coincide y la variable vinculada ‘Identifier’ tiene el mismo valor que la parte derecha apropiada del término.

{Identifier, Reason} = {error, "Database connection timed out."}.

Una operación de coincidencia de patrones falla cuando hay una discrepancia entre el término del lado derecho y el patrón del lado izquierdo. La siguiente coincidencia fallará, porque ‘Identifier’ está vinculado al valor ’error’, que no tiene una expresión adecuada en el término del lado derecho.

{Identifier, Reason} = {fail, "Database connection timed out."}.
> ** exception error: no match of right hand side value {fail,"Database ..."}