Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Erlang
Erlang
Resumen
Todo ello hace que Erlang sea un lenguaje muy apropiado para la programacin
de elementos de misin crtica, tanto a nivel de servidor como a nivel de
escritorio, e incluso para el desarrollo de sistemas embebidos o incrustados.
1
http://erlang.bosqueviejo.net/
2
http://creativecommons.org/licenses/by-nc-sa/3.0/
Tabla de contenidos
Prlogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii
Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
1. Acerca del autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix
2. Acerca de los Revisores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x
3. Acerca del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi
4. Objetivo del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii
5. A quin va dirigido este libro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
6. Estructura de la coleccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii
7. Nomenclatura usada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv
8. Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv
9. Ms informacin en la web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvi
1. Lo que debes saber sobre Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1. Qu es Erlang? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2. Caractersticas de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
3. Historia de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
4. Desarrollos con Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.1. Sector empresarial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
4.2. Software libre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5. Erlang y la Concurrencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.1. El caso de Demonware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5.2. Yaws contra Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2. El lenguaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1. Tipos de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.1. tomos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2. Nmeros Enteros y Reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.4. Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.5. Tuplas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.6. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2. Imprimiendo por pantalla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3. Fechas y Horas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3. Expresiones, Estructuras y Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1. Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.1. Expresiones Aritmticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.2. Expresiones Lgicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.3. Precedencia de Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2. Estructuras de Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.1. Concordancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.2. Estructura case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.3. Estructura if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.4. Listas de Comprensin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3. Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.1. Recoger excepciones: catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
iii
Erlang/OTP
iv
Erlang/OTP
v
Erlang/OTP
vi
Prlogo
Conoc a Manuel Angel cuando me explic su idea de escribir un libro
sobre Erlang en Castellano, no slo me pareci una idea apasionante,
sino un hito imprescindible para llevar este lenguaje de programacin a
donde se merece entre la comunidad castellano-parlante.
vii
Prlogo
viii
Introduccin
Sorprendernos por algo es el primer paso de la
mente hacia el descubrimiento.
Louis Pasteur
ix
Introduccin
x
Introduccin
xi
Introduccin
Por ltimo, el hecho de que el texto est en castellano hace que, sin duda,
sea ms asequible para el pblico de habla hispana. El nivel y densidad
de ciertas explicaciones son ms bajos cuando se tratan en el idioma
nativo, lo que hace que sea ms fcil de entender.
Hay muchos casos en los que una mala eleccin tecnolgica ha forzado
a reescribir, versin a versin, el desarrollo inicial. La motivacin a la
hora de seleccionar una tecnologa no puede ser nunca una moda o la
inercia. Aunque cada ao haya un nuevo lenguaje que ofrece versatilidad
y gran cantidad de facilidades, hay que tener siempre en mente que
un lenguaje puede estar orientado a resolver un problema determinado
ms adecuadamente que otros. En este punto hay que ser mucho ms
pragmticos que fanticos.
xii
Introduccin
Para los programadores nefitos ofrece una gua de aprendizaje base, una
forma rpida de adentrarse en el conocimiento del lenguaje que permite
comenzar a desarrollar directamente. Propone ejemplos, ejercicios y
preguntas que el programador puede realizar, resolver y responder.
6. Estructura de la coleccin
Al principio pens en escribir un nico libro orientado a Erlang, pero
viendo el tamao que estaba alcanzando pens que mejor era dividirlo
por temtica y darle a cada libro la extensin apropiada como para ser
ledo y consultado de forma fcil y rpida.
xiii
Introduccin
Nota
Recomiendo que, para poder hacer los ejemplos y practicar lo
que se va leyendo, se tenga a mano un ordenador con Erlang
instalado, as como acceso a su consola y un directorio en el
que poder ir escribiendo los programas de ejemplo. En este
caso ser de bastante ayuda revisar los apndices donde explica
cmo se descarga, instala y usa la consola de Erlang, as como la
compilacin de los ejemplos y su ejecucin de forma bsica.
7. Nomenclatura usada
A lo largo del libro encontrars muchos ejemplos y fragmentos de cdigo.
Los cdigos aparecen de una forma visible y con un formato distinto al
del resto del texto. Tendrn este aspecto:
-module(hola).
mundo() ->
io:format("Hola mundo!~n", []).
Nota
Esta es la forma que tendrn las notas informativas. Contienen
detalles o informacin adicional sobre el texto para satisfacer la
curiosidad del lector.
Importante
Estas son las notas importantes que indican usos especficos
y detalles importantes que hay que tener muy en cuenta. Se
recomienda su lectura.
xiv
Introduccin
8. Agradecimientos
Manuel ngel Rubio
Agradecer a mi familia, Marga, Juan Antonio y Ana Mara, por ser
pacientes y dejarme el tiempo suficiente para escribir, as como
su amor y cario. A mis padres por ensearme a defenderme en
esta vida, as como a competir conmigo mismo para aprender y
superarme en cada reto personal y profesional.
xv
Introduccin
9. Ms informacin en la web
Para obtener informacin sobre las siguientes ediciones, fe de erratas
y comentarios, contactos, ayuda y dems sobre el libro Erlang/OTP he
habilitado una seccin en mi web.
El sitio web:
http://erlang.bosqueviejo.net
xvi
Captulo 1. Lo que debes saber
sobre Erlang
Software para un mundo concurrente.
Joe Armstrong
1. Qu es Erlang?
Para comprender qu es Erlang, debemos entender que se trata de un
entorno o plataforma de desarrollo completa. Erlang proporciona no slo
el compilador para poder ejecutar el cdigo, sino que posee tambin una
coleccin de herramientas, y una mquina virtual sobre la que ejecutarlo,
por lo tanto existen dos enfoques:
1
Lo que debes
saber sobre Erlang
Erlang fue propietario hasta 1998 momento en que fue cedido como
cdigo abierto (open source) a la comunidad. Fue creado inicialmente por
Ericsson, ms especficamente por Joe Armstrong, aunque no slo por l.
2. Caractersticas de Erlang
Durante el perodo en el que Joe Armstrong y sus compaeros estuvieron
en los laboratorios de Ericsson, vieron que el desarrollo de aplicaciones
basadas en PLEX no era del todo ptimo para la programacin de
aplicaciones dentro de los sistemas hardware de Ericsson. Por esta razn
comenzaron a buscar lo que sera un sistema de desarrollo ptimo
basado en las siguiente premisas:
Distribuido
El sistema deba de ser distribuido para poder balancear su carga
entre los sistemas hardware. Se buscaba un sistema que pudiera
lanzar procesos no slo en la mquina en la que se ejecuta, sino
que tambin fuera capaz de hacerlo en otras mquinas. Lo que en
lenguajes como C viene a ser PVM o MPICH pero sin el uso explcito
de ninguna librera.
1
O trozos de cdigo nativo si se emplea HiPE.
2
Lo que debes
saber sobre Erlang
Tolerante a fallos
Si una parte del sistema tiene fallos y tiene que detenerse, que esto
no signifique que todo el sistema se detenga. En sistemas software
como PLEX o C, un fallo en el cdigo determina una interrupcin
completa del programa con todos sus hilos y procesos. Hay otros
lenguajes como Java, Python o Ruby que manejan estos errores
como excepciones, afectando slo a una parte del programa y
no a todos sus hilos. No obstante, en los entornos con memoria
compartida, un error puede dejar corrupta esta memoria por lo
que esa opcin no garantiza tampoco que no afecte al resto del
programa.
Escalable
Los sistemas operativos convencionales tenan problemas en
mantener un elevado nmero de procesos en ejecucin. Los
sistemas de telefona que desarrolla Ericsson se basan en tener
un proceso por cada llamada entrante, que vaya controlando los
estados de la misma y pueda provocar eventos hacia un manejador,
a su vez con sus propios procesos. Por lo que se buscaba un sistema
que pudiese gestionar desde cientos de miles, hasta millones de
procesos.
Tambin haba aspectos ntimos del diseo del lenguaje que se quisieron
tener en cuenta para evitar otro tipo de problemas. Aspectos tan
significativos como:
Asignaciones nicas
Como en los enunciados matemticos la asignacin de un valor a
una variable se hace una nica vez y, durante el resto del enunciado,
esta variable mantiene su valor inmutable. Esto nos garantiza un
mejor seguimiento del cdigo y una mejor deteccin de errores.
Lenguaje simple
Para rebajar la curva de aprendizaje el lenguaje debe de tener pocos
elementos y ninguna excepcin. Erlang es un lenguaje simple de
comprender y aprender, ya que tiene nada ms que dos estructuras
de control, carece de bucles y emplea tcnicas como la recursividad
y modularizacin para conseguir algoritmos pequeos y eficientes.
3
Lo que debes
saber sobre Erlang
Orientado a la Concurrencia
Como una especie de nueva forma de programar, este lenguaje se
orienta a la concurrencia de manera que las rutinas ms ntimas
del propio lenguaje estn preparadas para facilitar la realizacin de
programas concurrentes y distribuidos.
4
Lo que debes
saber sobre Erlang
3. Historia de Erlang
Joe Armstrong asisti a la conferencia de Erlang Factory de Londres, en
2010, donde explic la historia de la mquina virtual de Erlang. En s,
3
es la propia historia de Erlang/OTP. Sirvindome de las diapositivas que
proporcion para el evento, vamos a dar un repaso a la historia de Erlang/
OTP.
5
Lo que debes
saber sobre Erlang
Los procesos deban de ser una parte intrnseca del lenguaje, no una
librera o framework de desarrollo.
Nota
HiPE es el acrnimo de High Performance Erlang (Erlang de Alto
Rendimiento) que es el nombre de un grupo de investigacin
sobre Erlang formado en la Universidad de Uppsala en 1998. El
grupo desarroll un compilador de cdigo nativo de modo que la
mquina (BEAM) virtual de Erlang no tenga que interpretar ciertas
partes del cdigo si ya estn en lenguaje mquina mejorando as
su rendimiento.
6
Lo que debes
saber sobre Erlang
7
Lo que debes
saber sobre Erlang
Nota
Aprovechando que se ha comenzado a hacer esta lista de software
libre desarrollado en Erlang se ha estructurado y ampliado la
pgina correspondiente a Erlang en Wikipedia (en ingls de
momento y poco a poco en castellano), por lo que en estos
momentos ser ms extensa que la lista presente en estas
pginas.
6
https://klarna.com/
7
https://mikoagenda.com/es
8
http://aprendiendo-erlang.blogspot.com/p/donde-se-usa-erlang.html
8
Lo que debes
saber sobre Erlang
9
Apache CouchDB , es una base de datos documental con acceso a
datos mediante HTTP y empleando el formato REST. Es uno de los
proyectos que estn acogidos en la fundacin Apache.
10
Riak , una base de datos NoSQL inspirada en Dynamo (la base
de datos NoSQL de Amazon). Es usada por empresas como Mozilla
y Comcast. Se basa en una distribucin de fcil escalado y
completamente tolerante a fallos.
11
SimpleDB , tal y como indica su propia web (en castellano) es un
almacn de datos no relacionales de alta disponibilidad flexible que
descarga el trabajo de administracin de las bases de datos. Es decir,
un sistema NoSQL que permite el cambio en caliente del esquema
de datos de forma fcil que realiza auto-indexacin y permite la
distribucin de los datos. Fue desarrollada por Amazon.
12
Couchbase , es una base de datos NoSQL para sistemas de
misin crtica. Con replicacin, monitorizacin, tolerante a fallos y
compatible con Memcached.
Servidores Web
13
Yaws . Como servidor web completo, con posibilidad de instalarse
y configurarse para ello, slo existe (al menos es el ms conocido en
la comunidad) Yaws. Su configuracin se realiza de forma bastante
similar a Apache. Tiene unos scripts que se ejecutan a nivel de
servidor bastante potentes y permite el uso de CGI y FastCGI.
Frameworks Web
14
ErlyWeb , no ha tenido modificaciones por parte de Yariv desde
hace unos aos por lo que su uso ha decado. El propio Yariv lo
emple para hacer un clon de twitter y se emple inicialmente para
la interfaz de chat para facebook.
15
BeepBeep , es un framework inspirado en Rails y Merb aunque sin
integracin con base de datos.
16
Erlang Web , es un sistema desarrollado por Erlang Solutions que
trata igualmente las vistas y la parte del controlador pero tampoco
la parte de la base de datos.
9
http://couchdb.apache.org
10
http://wiki.basho.com/Riak.html
11
http://aws.amazon.com/es/simpledb/
12
http://www.couchbase.com/
13
http://yaws.hyber.org/
14
https://github.com/yariv/erlyweb
15
https://github.com/davebryson/beepbeep/
16
http://www.erlang-web.org/
9
Lo que debes
saber sobre Erlang
17
Nitrogen , es un framework pensado para facilitar la construccin
de interfaces web. Nos permite agregar cdigo HTML de una forma
simple y enlazarlo con funcionalidad de JavaScript sin necesidad de
escribir ni una sola lnea de cdigo JavaScript.
18
ChicagoBoss , quizs el ms activo y completo de los frameworks
web para Erlang a da de hoy. Tiene implementacin de vistas,
plantillas (ErlyDTL), definicin de rutas, controladores y modelos a
19
travs de un sistema ORM .
Chat
22
ejabberd , servidor de XMPP muy utilizado en el mundo Jabber.
Este servidor permite el escalado y la gestin de multi-dominios. Es
usado en sitios como la BBC Radio LiveText, Ovi de Nokia, KDE Talk,
Chat de Facebook, Chat de Tuenti, LiveJournal Talk, etc.
Colas de Mensajes
23
RabbitMQ , servidor de cola de mensajes muy utilizado en sistemas
de entornos web con necesidad de este tipo de sistemas para
conexiones de tipo websocket, AJAX o similar en la que se haga
necesario un comportamiento asncrono sobre las conexiones
sncronas. Fue adquirido por SpringSource, una filial de VMWare en
abril de 2010.
5. Erlang y la Concurrencia
Una de las mejores pruebas de que Erlang/OTP funciona, es mostrar las
comparaciones que empresas como Demonware o gente como el propio
Joe Armstrong han realizado. Sistemas sometidos a un banco de pruebas
17
http://nitrogenproject.com/
18
http://www.chicagoboss.org/
19
Object Relational Mapping, sistema empleado para realizar la transformacin entre objetos y tablas
para emplear directamente los objetos en cdigo y que la informacin que estos manejen se almacene
en una tabla de la base de datos.
20
http://zotonic.com/
21
Content Management System, Sistema de Administracin de Contenido
22
http://www.ejabberd.im/
23
http://www.rabbitmq.com/
10
Lo que debes
saber sobre Erlang
11
Lo que debes
saber sobre Erlang
12
Lo que debes
saber sobre Erlang
KB/s (eje Y) frente a carga (eje X). Las lneas que se cortan a partir de las 4
mil peticiones corresponden a dos configuraciones diferentes de Apache
(en negro y gris claro).
13
Captulo 2. El lenguaje
Slo hay dos tipos de lenguajes: aquellos de los que
la gente se queja y aquellos que nadie usa.
Bjarne Stroustrup
Erlang tiene una sintaxis muy particular. Hay gente a la que termina
gustndole y otras personas que lo consideran incmodo. Hay que
entender que es un lenguaje basado en Prolog y con tintes de Lisp por lo
que se asemeja ms a los lenguajes funcionales que a los imperativos.
14
El lenguaje
area(
Base,
Altura
) ->
Base * Altura
.
1. Tipos de Datos
En Erlang se manejan varios tipos de datos. Por hacer una distincin
rpida podemos decir que se distinguen entre: simples y complejos;
otras organizaciones podran conducirnos a pensar en los datos como:
escalares y conjuntos o atmicos y compuestos. No obstante, la forma
de organizarlos no es relevante con el fin de conocerlos, identificarlos
y usarlos correctamente. Emplearemos la denominacin simples y
complejos (o compuestos), pudiendo referirnos a cualquiera de las otras
formas de categorizacin si la explicacin resulta ms clara.
Como datos simples veremos en esta seccin los tomos y los nmeros.
Como datos de tipo complejo veremos las listas y tuplas. Tambin
veremos las listas binarias, un tipo de dato bastante potente de Erlang
y los registros, un tipo de dato derivado de las tuplas.
1.1. tomos
Los tomos son identificadores de tipo carcter que se emplean como
palabras clave y ayudan a semantizar el cdigo.
15
El lenguaje
> is_atom(cuadrado).
true
> is_atom(a4).
true
> is_atom(alta_cliente).
true
> is_atom(bajaCliente).
true
> is_atom(alerta_112).
true
> is_atom(false).
true
> is_atom('HOLA').
true
> is_atom(' eh??? ').
true
Hay tomos que se emplean con mucha frecuencia como son: true, false
y undefined.
Los tomos junto con los nmeros enteros y reales y las cadenas de texto
componen lo que se conoce en otros lenguajes como literales. Son los
datos que tienen un significado de por s, y se pueden asignar a una
variable directamente.
Nota
Como literales se pueden especificar nmeros, pero tambin
valores de representaciones de la tabla de caracteres. Al igual
que en otros lenguajes, Erlang permite dar el valor de un carcter
especfico a travs el uso de la sintaxis: $A, $1, $!. Esto retornar el
valor numrico para el smbolo indicado tras el smbolo del dlar
en la tabla de caracteres.
> is_float(5).
false
> is_float(5.0).
true
> is_integer(5.0).
false
> is_integer(5).
16
El lenguaje
true
Nota
Los nmeros se pueden indicar tambin anteponiendo la base
en la que queremos expresarlos y usando como separador la
almohadilla (#). Por ejemplo, si queremos expresar los nmeros
en base octal, lo haremos anteponiendo la base al nmero que
queremos representar 8#124. Anlogamente 2#1011 representa
un nmero binario y 16#f42a un nmero hexadecimal.
1.3. Variables
Las variables, como en matemticas, son smbolos a los que se enlaza un
valor y slo uno a lo largo de toda la ejecucin del algoritmo especfico.
Esto quiere decir que cada variable durante su tiempo de vida slo puede
contener un valor.
> Pi = 3.1415.
3.1415
> Telefono = "666555444".
"666555444"
> Depuracion = true.
true
> Base = 2.
2
17
El lenguaje
> Base = 2.
2
> Base = 3.
** exception error: no match of right hand side value 3
Nota
Para nuestras pruebas, a nivel de consola y para no tener que salir
y entrar cada vez que queramos que Erlang olvide el valor con el
que se enlaz una variable, podemos emplear:
> f(Base).
ok
> Base = 3.
3
1.4. Listas
Las listas en Erlang son vectores de informacin heterognea, es decir,
pueden contener informacin de distintos tipos, ya sean nmeros,
tomos, tuplas u otras listas.
18
El lenguaje
> [ 1, 2, 3, 4, 5 ].
[1,2,3,4,5]
> [ 1, "Hola", 5.0, hola ].
[1,"Hola",5.0,hola]
19
El lenguaje
[1]
Importante
Esta forma de tratar las cadenas es muy similar a la que se emplea
en lenguaje C, en donde el tipo de dato char es un dato de 8
bits en el que se puede almacenar un valor de 0 a 255 y que
las funciones de impresin tomarn como representaciones de
la tabla de caracteres en uso por el sistema. En Erlang, la nica
diferencia es que cada dato no es de 8 bits sino que es un
entero lo que conlleva un mayor consumo de memoria pero mejor
soporte de nuevas tablas como la de UTF-16 o las extensiones del
UTF-8 y similares.
20
El lenguaje
> <<"Hola">>.
<<"Hola">>
> <<72,111,$l,$a>>.
<<"Hola">>
La lista binaria no tiene las mismas funcionalidades que las listas vistas
anteriormente. No se pueden agregar elementos ni emplear el formato
de anexin y supresin de elementos tal y como se haba visto antes.
Pero se puede hacer de otra forma ms potente.
21
El lenguaje
<<"Hola mundo!">>
> byte_size(A).
5
> byte_size(B).
6
> byte_size(C).
11
El tamao est ligado al tipo, ya que una unidad de medida no es nada sin
su cuantizador. En este caso, el cuantizador (o tipo) que hemos elegido
es binary. Este tipo indica que la variable ser de tipo lista binaria, con lo
que el tamao ser referente a cuntos elementos de la lista contendr
la variable.
> <<1215261793:32/big>>.
<<"Hola">>
> <<1215261793:32/little>>.
22
El lenguaje
<<"aloH">>
> <<1215261793:32/native>>.
<<"Hola">>
Nota
Para obtener el tamao de la lista binaria en bits podemos
emplear la funcin bit_size/1 que nos retornar el tamao de
la lista binaria:
1.5. Tuplas
Las tuplas son tipos de datos organizativos en Erlang. Se pueden crear
listas de tuplas para conformar conjuntos de datos homogneos de
elementos individuales heterogneos.
23
El lenguaje
Nota
En el ejemplo se puede ver que la fecha y hora se ha introducido
de una forma un tanto peculiar. En Erlang, las funciones de los
mdulos de su librera estndar, trabajan con este formato, y si se
emplea, es ms fcil tratar y trabajar con fechas. Por ejemplo, si
ejecutsemos:
Este tipo de dato tambin se emplea para emular los arrays asociativos
(o hash). Estos arrays almacenan informacin de forma que sea posible
rescatarla mediante el texto o identificador especfico que se us para
almacenarla. Se usa en aquellos casos en que es ms fcil que acceder
al elemento por un identificador conocido que por un ndice que podra
ser desconocido.
24
El lenguaje
true
> Meses = [
{enero, 31}, {febrero, 28}, {marzo, 31},
{abril, 30}, {mayo, 31}, {junio, 30},
{julio, 31}, {agosto, 31}, {septiembre, 30},
{octubre, 31}, {noviembre, 30}, {diciembre, 31}
].
> proplists:get_value(enero, Meses).
31
> proplists:get_value(junio, Meses).
30
Nota
El mdulo de proplists contiene muchas ms funciones tiles para
tratar este tipo de coleccin de datos de forma fcil. No es mala
idea dar un repaso al mismo para ver el partido que podemos
sacarle en nuestros programas.
1.6. Registros
Los registros son un tipo especfico de tupla que facilita el acceso a los
datos individuales dentro de la misma mediante un nombre y una sintaxis
de acceso mucho ms cmoda para el programador. Internamente para
Erlang, los registros realmente no existen. A nivel de preprocesador son
intercambiados por tuplas. Esto quiere decir que los registros en s son
una simplificacin a nivel de uso de las tuplas.
25
El lenguaje
Nota
Los ficheros de cdigo de Erlang normalmente tiene la extensin
erl, sin embargo, cuando se trata de cdigos de tipo cabecera,
estos ficheros mantienen una extensin a medio camino entre
los de cabecera de C (que tienen la extensin .h) y los de cdigo
normales de Erlang. Su extensin es: hrl. En estos ficheros se
introducirn normalmente definiciones y registros.
Veamos con una pequea prueba que si creamos una tupla A Erlang
la reconoce como tupla de cuatro elementos. Si cargamos despus el
archivo registros.hrl cuyo contenido es la definicin del registro
agenda el tratamiento de la tupla se modifica automticamente y
ya podemos emplear la notacin para registros de los ejemplos
subsiguientes:
Nota
Para obtener la posicin dentro de la tupla de un campo, basta
con escribirlo de la siguiente forma:
#agenda.nombre
> A#agenda.nombre.
26
El lenguaje
"Manuel"
> A#agenda.telefono.
666666666
> A#agenda{telefono=911232323}.
#agenda{nombre = "Manuel",apellidos = "Rubio",
telefono = 911232323}
> #agenda{nombre="Juan Antonio",apellidos="Rubio"}.
#agenda{nombre = "Juan Antonio",apellidos = "Rubio",
telefono = undefined}
Importante
Como se ha dicho anteriormente, los registros son entidades
que trabajan a nivel de lenguaje pero Erlang no los contempla
en tiempo de ejecucin. Esto quiere decir que el preprocesador
trabaja para convertir cada instruccin concerniente a registros
para que sean relativas a tuplas y por tanto la funcin record_info
no se puede emplear con variables. Algo como lo siguiente:
Como los registros son internamente tuplas cada campo puede contener
a su vez cualquier otro tipo de dato, no slo tomos, cadenas de texto
o nmeros, sino tambin otros registros, tuplas o listas. Con ello, esta
estructura nos propone un sistema organizativo interesante para poder
acceder directamente al dato que necesitemos en un momento dado
facilitando la labor del programador enormemente.
27
El lenguaje
Para ello tenemos el mdulo io, del que emplearemos de momento slo
la funcin format. Esta funcin nos permite imprimir por pantalla la
informacin que queramos mostrar basado en un formato especfico que
se pasa como primer parmetro.
Nota
Para los que hayan programado con lenguajes tipo C, Java, PHP, ...
esta funcin es equivalente y muy parecida a printf, es decir, la
funcin se basa en una cadena de texto con un formato especfico
(agregando parmetros) que sern sustituidos por los valores que
se indiquen en los parmetros siguientes.
~
Imprime el smbolo de la virgulilla.
c
Representa un carcter que ser reemplazado por el valor
correspondiente pasado en la lista como segundo parmetro. Antes
de la letra c se pueden agregar un par de nmeros separados
por un punto. El primer nmero indica el tamao del campo y
la justificacin a izquierda o derecha segn el signo positivo o
negativo del nmero. El segundo nmero indica las veces que se
repetir el caracter. Por ejemplo:
28
El lenguaje
e/f/g
Se encargan de presentar nmeros en coma flotante. El formato de
e es cientfico (X.Ye+Z) mientras que f lo presenta en formato con
coma fija. El formato g es una mezcla ya que presenta el formato
cientfico si el nmero se sale del rango [0.1,10000.0], y en caso
contrario presenta el formato como si fuese e. Los nmeros que se
pueden anteponer a cada letra indican, el tamao que se quiere
representar y justificacin (como se vi antes). Tras el punto la
precisin. Unos ejemplos:
s
Imprime una cadena de caracteres. Similar a c, pero el significado
del segundo nmero en este caso es la cantidad de caracteres de la
lista que se mostrar. Veamos algunos ejemplos:
w/W
Imprime cualquier dato con su sintaxis estandar. Se usa sobretodo
para poder imprimir tuplas, pero imprime igualmente listas,
nmeros, tomos, etc. La nica salvedad, es que una cadena
de caracteres ser considerada como una lista. Los nmeros de
anteposicin se emplean de la misma forma que en s. Un ejemplo:
29
El lenguaje
p/P
Es igual que w, pero intenta detectar si una lista es una cadena de
caracteres para imprimirla como tal. Si la impresin es demasiado
grande, la parte en varias lneas. La versin en mayscula, tambin
es igual a su homnimo W, aceptando un parmetro extra para
profundidad.
b/B/x/X/+/#
Imprimen nmeros segn la base indicada. Los nmeros anteriores
a cada letra (o smbolo) indican, el primero la magnitud y
justificacin de la representacin y el segundo la base en la que se
expresar el nmero. La diferencia entre ellos es que B imprime slo
la representacin numrica.
i
Ignora el parmetro que toque emplear. Es til si el formato de
los parmetros que se pasa es siempre el mismo y en un formato
especfico se desea ignorar uno concreto.
n
Retorno de carro, hace un salto de lnea, de modo que se pueda
separar por lneas diferentes lo que se desee imprimir por pantalla.
Nota
Existe tambin el mdulo io_lib que dispone tambin de la
funcin format. La nica diferencia que presenta, es que en lugar
de presentar por pantalla la cadena resultante, la retorna como
cadena de caracteres.
30
El lenguaje
3. Fechas y Horas
El manejo de fechas y horas en Erlang no se realiza con un tipo estndar,
sino que se establece como un trmino encerrado en una tupla. Una
fecha tiene la siguiente forma de tupla:
{2012,5,22}
Es una tupla compuesta por tres campos enteros destinados al ao, mes
y da, en ese orden. La funcin interna date/0 retorna este formato, pero
hay ms funciones de tratamiento de fecha que emplean este formato.
{22,10,5}
> erlang:localtime().
{{2012,5,22},{22,10,5}}
Hay otras funciones como now/0, que retornan la fecha y hora actuales
2
en formato POSIX , en una tupla {MegaSeconds, Seconds, MicroSeconds},
lo que quiere decir que el clculo de la hora en un slo entero sera as:
Por ltimo, indicar que las fechas tambin pueden ser convertidas
o empleadas en formato UTC (o GMT). Podemos convertir una
fecha a formato UTC (erlang:localtime_to_universaltime/1) o
viceversa (erlang:universaltime_to_localtime/1).
2
El formato de POSIX para fecha y hora consiste en un nmero entero que corresponde al nmero de
segundos transcurrido desde el 1 de enero de 1970 hasta la fecha que se indique.
31
El lenguaje
Nota
El mdulo calendar provee una serie de funciones que
permiten averiguar si el ao introducido es bisiesto
(is_leap_year/1), el da de la semana de una fecha concreta
(iso_week_number/0 e iso_week_number/1), el ltimo da
del mes (last_day_of_the_month/2) y ms an.
3
La toma de segundos siempre es en formato UTC (o GMT), por lo que las fechas que se proporcionen para
la conversin a segundos, sern tomadas como en hora local y convertidas a UTC antes de su conversin
a segundos.
32
Captulo 3. Expresiones,
Estructuras y Excepciones
La mejor forma de predecir el futuro es
implementarlo.
David Heinemeier Hansson
1. Expresiones
Las expresiones son la conjuncin de smbolos con datos para conformar
una sentencia vlida para el lenguaje con significado para el compilador,
de modo que pueda ofrecer, en tiempo de ejecucin, una representacin
a nivel de cdigo mquina del resultado que se pretende obtener.
> 2 + 2.
4
> 2 - 2.
0
> 2 * 3.
6
> 10 / 3.
3.3333333333333335
> 10 div 3.
3
> 10 rem 3.
1
33
Expresiones, Estructuras
y Excepciones
> 2 * 3 + 1.
7
> 2 * (3 + 1).
8
> 3 * 3 * 3.
27
> 1 bxor 2.
3
> 1 bxor 3.
2
> 3 band 6.
2
> 2#011 bor 2#100.
7
> (bnot 2#101) band 2#11.
2
Estas herramientas nos facilitan operar de forma binaria con los nmeros.
> C1 = 2 > 1.
true
> C2 = 1 > 2.
false
> C1 and C2.
false
> C1 or C2.
true
> C3 = 3 =:= (1 + 2).
true
> C1 and (C2 or C3).
true
34
Expresiones, Estructuras
y Excepciones
Nota
Adems de los operadores and y or, en Erlang existen otros como
andalso y orelse. El resultado a nivel de clculo es el mismo. Lo
nico que vara es que los primeros realizan una comprobacin
absoluta de los valores pasados, evaluando y comparando todos
los valores, mientras que los presentados recientemente, realizan
una comprobacin vaga.
Operador Descripcin
: Ejecucin de funciones
# Resolucin de registros
+ - bnot not Unitarios
/ * div rem band and Divisin, Multiplicacin e Y lgico.
+ - bor bxor bsl bsr or xor Suma, resta y O inclusivo y
exclusivo.
++ -- Agrega/Sustrae de conjuntos/
listas.
== /= =< < >= > =:= =/= Comparaciones
35
Expresiones, Estructuras
y Excepciones
Operador Descripcin
andalso Y lgico con comprobacin vaga
orelse O lgico con comprobacin vaga
=! Asignacin y Paso de mensaje
catch Captura de errores
2. Estructuras de Control
A diferencia de los lenguajes imperativos en Erlang slo hay dos
estructuras de control: if y case; aunque se puedan parecer a las
estructuras que existen en otros lenguajes, difieren.
Como el que encajen los valores es tan importante para estas estructuras,
y para la mayora de estructuras en ejecucin dentro de la programacin
de Erlang, en general, dedicaremos una parte a estudiar lo que
llamaremos a partir de ahora como concordancia y seguidamente
veremos las estructuras donde se aplica.
2.1. Concordancia
En este apartado revisaremos un aspecto bastante importante en lo que
respecta a la programacin en Erlang y que conviene tener interiorizado,
lo que facilitar mucho la programacin en este lenguaje. Me refiero a la
concordancia (en ingls match). Podramos definir esta expresin como la
cualidad de una estructura de datos de asemejarse a otra, incluso aunque
haya que aplicar asignacin para ello.
[1,2,3] = [1,2,3]
[A,B,C] = [1,2,3]
36
Expresiones, Estructuras
y Excepciones
{A,_,C} = {1,2,3}
Veamos un ejemplo:
37
Expresiones, Estructuras
y Excepciones
{D,M,A} ->
integer_to_list(A) ++ "-" ++
integer_to_list(M) ++ "-" ++
integer_to_list(D);
<<Dia:2/binary,"/",Mes:2/binary,"/",Agno:4/binary>> ->
binary_to_list(Agno) ++ "-" ++
binary_to_list(Mes) ++ "-" ++
binary_to_list(Dia);
_ ->
""
end.
Con esto nos aseguramos de que los valores que se parsearn dentro de
cada bloque son del tipo que se esperan, y que algo como una tupla que
contenga listas de caracteres no haga fallar el primer bloque de opcin.
Para las guardas se pueden emplear tanto "," como and, o andalso, en
caso de que se quiera el comportamiento del y lgico; o ";", or o orelse,
para conseguir el comportamiento del o inclusivo lgico.
38
Expresiones, Estructuras
y Excepciones
La diferencia entre los signos "," y ";" con andalso y orelse es que
los signos capturan excepciones. Es decir mediante el uso de los
signos de puntuacin se ignorarn los fallos que puedan suceder en la
evaluacin, continuando con la evaluacin de lo siguiente. Para aclarar
mejor las diferencias veamos tres ejemplos de cdigo similares pero que
funcionan de forma bastante diferente:
> case a of
> _ when (a+1)=:=a or b=:=b -> ok;
> _ -> fail
> end.
* 1: syntax error before: '=:='
> case a of
> _ when (a+1)=:=a orelse b=:=b -> ok;
> _ -> fail
> end.
fail
> case a of
> _ when (a+1)=:=a ; b=:=b -> ok;
> _ -> fail
> end.
ok
2.3. Estructura if
Otra de las estructuras que se puede emplear con Erlang es if. Esta
estructura guarda cierta similitud con las que se emplean en los
lenguajes imperativos, salvo porque debe existir una opcin de cdigo
que sea ejecutable en caso de que la clusula previa se cumpla; adems
y en todo caso que se debe retornar siempre un valor.
39
Expresiones, Estructuras
y Excepciones
Importante
En otros lenguajes, el operador de mayor que (>) y menor que
(<) se sita siempre antes del signo igual, mientras que, como se
vio en la tabla de precedencia de operadores, segn si es uno u
otro, se coloca de modo que apunte siempre hacia el smbolo de
igualdad.
> Caso = if
> D >= 1 andalso D =< 10 -> caso1;
> D >= 11 andalso D =< 20 -> caso2;
> true -> unknown
> end.
40
Expresiones, Estructuras
y Excepciones
Por ejemplo, si queremos sacar de una lista slo los nmeros pares, sera
tan sencillo como:
Nota
Las listas de comprensin son uno de los elementos ms
importantes del lenguaje, por lo que conviene que se tenga
muy presente su forma, la utilidad que tienen con respecto a la
seleccin y proyeccin de informacin y realizar pruebas hasta
comprender su funcionamiento completa y correctamente.
> A = [[1,1],[2,2],[3,3],[4,4],[5,5],[6,6]].
[[1,1],[2,2],[3,3],[4,4],[5,5],[6,6]]
41
Expresiones, Estructuras
y Excepciones
3. Excepciones
Erlang es tolerante a fallos. Esto le viene dado por el empleo de procesos
en lugar de hilos. Si un proceso muere y deja su estado de memoria
corrupto no afectar a otros procesos, ya que ni siquiera comparten
memoria (cada proceso tiene la suya propia y es otra de las propiedades
de Erlang el nada compartido o share nothing en ingls), ni la ejecucin
de uno est condicionada o afecta a otros procesos.
> 1 = a.
** exception error: no match of right hand side value a
> catch 1 = a.
{'EXIT',{{badmatch,a},[{erl_eval,expr,3}]}}
42
Expresiones, Estructuras
y Excepciones
Importante
En este caso es una mala idea haber capturado la excepcin ya
que tapa un error de cdigo que hemos provocado y que, gracias
a catch, hace que consideremos el cdigo como correcto, cuando
no es as.
> 2+3=5.
5
43
Expresiones, Estructuras
y Excepciones
> try
> a = 1
> catch
> throw:Term -> Term;
> exit:Razon -> Razon;
> error:Razon -> Razon
> end.
{badmatch,1}
> try
> a=1
> catch
> error:Error -> Error
> after
> io:format("Adios~n")
> end.
Adios
{badmatch,1}
44
Expresiones, Estructuras
y Excepciones
Nota
Podramos profundizar ms en estas estructuras, pero lo dejo
en este punto porque me gusta ms la filosofa de Erlang: let
it crash (deja que falle); que indica que el sistema debe de
poder fallar para volver a iniciar su ejecucin de forma normal,
ya que mantenerse en ejecucin tras un fallo podra provocar
una situacin imprevista que, adems, se prolongase, con lo que
dificultara an ms la deteccin del fallo.
function_clause
Cuando se llama a una funcin con parmetros incorrectos, ya sea
en nmero, concordancia o por guardas, se dispara esta excepcin:
case_clause
Prcticamente igual la anterior. Este se dispara cuando no hay
concordancia con ningn bloque (y sus guardas, en caso de que
tuviese), dentro de la clusula case.
if_clause
Al igual que el resto de *_clause, este error se dispara cuando no
hay ninguna guarda del if aplicable. El sistema indicar que no hay
rama true disponible, ya que es una prctica habitual el disponer
de la misma.
badmatch
Suelen suceder cuando falla la concordancia (matching), ya sea al
intentar asignar una estructura de datos sobre otra que no tiene la
misma forma o cuando se intenta hacer una asignacin sobre una
variable que ya tiene un valor.
45
Expresiones, Estructuras
y Excepciones
badarg
Se suele disparar cuando llamamos a una funcin con argumentos
errneos. A diferencia de las ya vistas esta excepcin es introducida
como una validacin de argumentos por el programador fuera de las
guardas, por lo que para emplearla, debemos de crear un bloque en
nuestras funciones de validacin de argumentos que, en caso de no
ser correctos, la lancen. Un ejemplo de funcin que dispone de esto:
> io:format({hola}).
** exception error: bad argument
undef
Lanzada cuando se llama a una funcin que no est definida (no
existe), ya sea por su nmero de parmetros o por su nombre dentro
del mdulo:
> lists:no_existe().
** exception error: undefined function lists:no_existe/0
badarith
Esta excepcin es para errores matemticos (aritmticos). Sucede
cuando se intenta realizar una operacin con valores incorrectos
(como una suma de un nmero con una lista) o divisiones por cero.
Un ejemplo:
> 27 / 0.
** exception error: bad argument in an arithmetic expr...
badfun
Sucede cuando se intenta emplear una variable que no contiene
una funcin. Un ejemplo:
badarity
Es un caso especfico de badfun, en este caso el error es debido a
que a la funcin que contiene la variable, se le pasa un nmero de
argumentos que no puede manejar, porque son ms o menos de los
que soporta. Un ejemplo:
46
Expresiones, Estructuras
y Excepciones
system_limit
Se alcanz el lmite del sistema. Esto puede pasar cuando:
tenemos demasiados procesos limitados por el parmetro de
procesos mximos (se puede ampliar), o demasiados argumentos
en una funcin, tomos demasiado grandes o demasiados tomos,
demasiados nodos conectados, etc. Para una mejor optimizacin
del sistema y entendimiento del mismo podemos leer la Gua de
2
Eficiencia de Erlang (en ingls) .
Importante
Hay que tener especial cuidado con los errores de system_limit.
Son lo suficientemente graves como para parar todo el sistema (la
mquina virtual de Erlang al completo).
{Error, Reason}
Nota
A partir de la versin de Erlang R15, en Reason se puede ver
adems el nombre del fichero y nmero de lnea en el se realiz
la llamada, lo cual facilita la deteccin de errores.
2
http://www.erlang.org/doc/efficiency_guide/advanced.html
47
Captulo 4. Las funciones y
mdulos
Divide y vencers.
Refrn popular
-module(mi_modulo).
-export([mi_funcion/0]).
mi_funcion() ->
"Hola mundo!".
48
Las funciones y mdulos
-module(mi_modulo).
-compile([export_all]).
Nota
Una vez tengamos el fichero creado, compilarlo es tan sencillo
como ir a una consola del sistema operativo y ejecutar:
erlc mi_modulo.erl
> c(mi_modulo)
> mi_modulo:mi_funcion().
"Hola mundo!"
49
Las funciones y mdulos
Importante
A diferencia de otros lenguajes donde los paquetes, mdulos
o libreras se pueden encontrar de modo jerrquico, Erlang
establece el nombre de sus mdulos de forma plana. Esto quiere
decir que si existe un mdulo llamado mi_modulo e intentamos
cargar otro mdulo con el mismo nombre, se empleara el que
tuviese la fecha de compilacin ms reciente.
-module(traductor).
-export([get/1]).
-import(proplists, [get_value/2]).
data() ->
[{"hi", "hola"}, {"bye", "adios"}].
get(Key) ->
get_value(Key, data()).
50
Las funciones y mdulos
Nota
La importacin es una tcnica que puede hacer confuso el
cdigo escrito. Se recomienda no emplearla a menos que el uso
masificado de la funcin en cuestin sea ms beneficioso para la
lectura del cdigo que invocarla de manera fully qualified.
3. Polimorfismo y Concordancia
Una de las particularidades de las funciones de Erlang, es que disponen
de polimorfismo. Si tuvisemos que programar una funcin que tuviese
algunos de sus parmetros con valores por defecto, podramos emplear
el polimorfismo tal y como se da en muchos otros lenguajes imperativos,
definiendo dos funciones con distinto nmero de parmetros, de la
siguiente forma:
multiplica(X, Y) ->
X * Y.
multiplica(X, Y, Z) ->
X * Y * Z.
51
Las funciones y mdulos
Importante
Cuando se emplea el polimorfismo, es decir la declaracin de
un mismo nombre de funcin para igual nmero de parmetros
pero diferente contenido, se debe de separar la definicin de una
funcin de la siguiente a travs del punto y coma (;), mientras
que la ltima definicin debe de llevar el punto final. Esto es
as para que los bloques de funciones polimrficas de este tipo
estn siempre agrupados, conformando una nica estructura ms
legible.
4. Guardas
Anteriormente ya vimos las guardas en las estructuras de control case
e if. Como la estructura de funcin es tan similar a las estructuras de
control, tambin contempla el uso de guardas, lo que le permite realizar
un polimorfismo todava ms completo.
5. Clausuras
Si revisamos un momento la teora lo que ahora vamos a ver podra
encajar perfectamente como clausura, lambda o funcin annima. En
principio, las definiciones:
52
Las funciones y mdulos
Por otro lado, tenemos el clculo lambda, inventado por Alonzo Church
y Stephen Kleen en 1930, que en un entorno matemtico define lo
que es una funcin para abstraer las ecuaciones en un lenguaje ms
simplificado (Peter Landin se encarg de llevar esta teora a Algol 60). El
caso es que la teora de funciones, subprogramas y subrutinas se basa
en esta teora, pero el nombre lambda, en lenguajes imperativos ha sido
otorgado a funciones annimas.
Podemos hacer tambin que una funcin normal, o incluso una annima,
nos retorne una funcin especfica que haga una accin concreta segn
los datos con los que haya sido llamada la primera:
53
Las funciones y mdulos
-module(clausura).
-compile([export_all]).
Nota
Referenciar una funcin definida de forma normal como una
funcin annima o clausura se consigue de la siguiente forma:
F = fun io:format/1.
54
Las funciones y mdulos
-module(infinitos).
-compile([export_all]).
enteros(Desde) ->
fun() ->
[Desde|enteros(Desde+1)]
end.
> E = infinitos:enteros(5).
#Fun<infinitos.0.16233373>
> [N|F] = E().
[5|#Fun<infinitos.0.16233373>]
> [M|G] = F().
[6|#Fun<infinitos.0.16233373>]
6. Programacin Funcional
Cuando se piensa en programacin funcional, normalmente, se piensa
en las listas de comprensin y en funciones sobre listas como son map,
filter o fold.
map/2
Se ejecuta la clausura pasada como parmetro, recibiendo cada
elemento de la lista como parmetro y retornando un valor por cada
llamada que ser almacenado y retornado por map/2 al final de la
ejecucin de todos los elementos. Por ejemplo:
> L = [1,2,3,4].
55
Las funciones y mdulos
any/2
Se evala cada elemento con la clausura pasada como parmetro,
debiendo retornar sta true o false. Si alguno de los elementos
retorna true, la funcin any/2 retorna tambin true. Un ejemplo:
> L = [1,2,3,4].
> lists:any(fun(X) ->
> if
> X > 2 -> true;
> true -> false
> end
> end, L).
true
all/2
Igual que la anterior, con la salvedad de que todos los elementos
evaluados deben retornar true. En el momento en el que uno
retorne false, la funcin all/2 retornara false. Un ejemplo:
> L = [1,2,3,4].
> lists:all(fun(X) ->
> if
> X > 2 -> true;
> true -> false
> end
> end, L).
false
foreach/2
Aplica la ejecucin de la clausura a cada elemento de la lista. En
principio es igual que map/2, salvo que foreach/2 no guarda el
retorno de las clausuras que ejecuta ni lo retorna. Por ejemplo:
> L = [1,2,3,4].
> lists:foreach(fun(X) -> io:format("~p~n", [X]) end, L).
1
2
3
4
ok
foldl/3 - foldr/3
Esta funcin se encarga de ejecutar la clausura pasando como
parmetro el elemento de la lista y el retorno de la ejecucin
anterior. Es como si encadenase la ejecucin de las clausuras, que
forzosamente deben aceptar los dos parmetros. La ltima letra
56
Las funciones y mdulos
> L = [1,2,3,4],
> F = fun(X, Factorial) -> Factorial * X end,
> lists:foldl(F, 1, L).
24
mapfoldl/3 - mapfoldr/3
Estas funciones son una combinacin de map/2 y fold/3.
Encadenan los resultados de cada una de las clausuras de la
anterior a la siguiente comenzando por un valor inicial, guardando
el resultado de ejecucin de cada clausura. El retorno de la funcin
clausura debe ser una tupla en la que el primer valor es el resultado
de la parte map/2 y el segundo valor es el retorno para seguir
encadenando. El retorno de ambas funciones es tambin una tupla
en la que el primer elemento es una lista con todos los elementos
(tal y como lo hara map/2) y el segundo valor es el resultado de la
parte de fold/3. Un ejemplo:
> L = [1,2,3,4],
> F = fun(X, Factorial) -> {X*2, Factorial*X} end,
> lists:mapfoldl(F, 1, L).
{[2,4,6,8],24}
filter/2
El filtrado toma la lista inicial y ejecuta la clausura para cada
elemento. La clausura debe retornar verdadero o falso (true o false).
Cada elemento que cumpla con la clausura ser agregado a la lista
del resultado de filter/2. Un ejemplo:
> L = [1,2,3,4],
> F = fun(X) -> if X > 2 -> true; true -> false end end,
> lists:filter(F, L).
[3,4]
takewhile/2
En este caso, la clausura se emplea como filtro al igual que con
filter/2, pero en el momento en el que un valor retorna falso
termina la ejecucin. Por ejemplo:
> L = [1,2,3,4],
> F = fun(X) -> if X =< 2 -> true; true -> false end end,
> lists:takewhile(F, L).
57
Las funciones y mdulos
[1,2]
dropwhile/2
Este es el complementario de takewhile. No toma ningn
elemento mientras se cumpla la condicin. En el momento que se
incumple la condicin, toma todos los elementos desde ese punto
hasta el final. Es decir, que toma todos los elementos que no tomara
takewhile/2. Un ejemplo:
> L = [1,2,3,4],
> F = fun(X) -> if X =< 2 -> true; true -> false end end,
> lists:dropwhile(F, L).
[3,4]
splitwidth/2
Divide la lista en dos sublistas de manera equivalente a introducir
en una tupla como primer valor el resultado de takewhile/2 y
como segundo valor el resultado de dropwhile/2. Un ejemplo:
> L = [1,2,3,4],
> F = fun(X) -> if X =< 2 -> true; true -> false end end,
> lists:splitwith(F, L).
{[1,2],[3,4]}
7. Recursividad
La recursividad define el hecho de que una funcin se pueda llamar a
s misma para completar el procesamiento sobre una muestra de datos
a la que se puede aplicar el mismo algoritmo de forma recurrente hasta
conseguir una solucin final.
58
Las funciones y mdulos
Nota
Erlang implementa un sistema denominado tail recursion (o
recursividad de cola), que hace que la pila de una llamada a
la siguiente se libere dado que el cdigo para ejecutar en esa
funcin ya no es necesario. Esto evita que se produzcan errores
por desbordamiento de pila, convirtiendo el cdigo recursivo en
iterativo, al menos a efectos de consumo de memoria.
-module(fact).
-compile(export_all).
fact(0) -> 1;
fact(X) -> X * fact(X-1).
> L = [5,2,8,4,3,2,1].
> {L1,L2} = lists:split(length(L) div 2, L).
{[5,2,8],[4,3,2,1]}
59
Las funciones y mdulos
mezcla([], L) ->
L;
mezcla(L, []) ->
L;
mezcla([H1|T1]=L1, [H2|T2]=L2) ->
if
H1 =< H2 -> [H1|mezcla(T1,L2)];
true -> [H2|mezcla(L1,T2)]
end.
ordena([]) ->
[];
ordena([H]) ->
[H];
ordena(L) ->
{L1,L2} = separa(L),
mezcla(ordena(L1), ordena(L2)).
-module(mergesort).
-export([ordena/1]).
separa(L) ->
lists:split(length(L) div 2, L).
60
Las funciones y mdulos
mezcla([], L) ->
L;
mezcla(L, []) ->
L;
mezcla([H1|T1]=L1, [H2|T2]=L2) ->
if
H1 =< H2 -> [H1|mezcla(T1,L2)];
true -> [H2|mezcla(L1,T2)]
end.
ordena([]) ->
[];
ordena([H]) ->
[H];
ordena(L) ->
{L1,L2} = separa(L),
mezcla(ordena(L1), ordena(L2)).
> mergesort:ordena([1,7,5,3,6,2]).
[1,2,3,5,6,7]
-module(quicksort).
61
Las funciones y mdulos
-export([ordena/1]).
separa([]) ->
{[], [], []};
separa([H]) ->
{[H], [], []};
separa([Pivote|T]) ->
Menor = [ X || X <- T, X =< Pivote ],
Mayor = [ X || X <- T, X > Pivote ],
{Menor, [Pivote], Mayor}.
ordena([]) ->
[];
ordena([H]) ->
[H];
ordena(L) ->
{L1, [Pivote], L2} = separa(L),
mezcla(ordena(L1) ++ [Pivote], ordena(L2)).
> quicksort:ordena([1,7,5,3,6,2]).
[1,2,3,5,6,7]
8. Funciones Integradas
En Erlang existen funciones que no estn escritas en Erlang, sino que
el sistema las procesa a bajo nivel y forman parte de la mquina
virtual como instrucciones base que se ejecutan mucho ms rpido.
Estas funciones construidas en el sistema se albergan bajo el mdulo
erlang. Normalmente no hace falta referirse al mdulo para emplearlas
(a menos que exista ambigedad). Algunas de ellas ya las hemos
visto: is_integer/1, integer_to_list/1, length/1, e incluso las
operaciones matemticas, lgicas y otras. Un ejemplo:
62
Las funciones y mdulos
Nota
Robert Virding, uno de los creadores/fundadores/inventores de
2
Erlang, coment en un artculo de su blog , lo confuso que resulta
determinar qu es un BIF y qu no. Un intento de definirlo
por parte de Jonas Barklund y Robert Virding disponible en la
especificacin (no indica URL especfica el autor en su blog), es
que un BIF fue una parte del lenguaje Erlang que no dispona de
una sintaxis concreta o especial, por lo que se mostraba como una
llamada a funcin normal.
2
http://rvirding.blogspot.com.es/2009/10/what-are-bifs.html
63
Captulo 5. Procesos
Cuando ests en un atasco de trfico con un
Porsche, todo lo que puedes hacer es consumir
ms combustible que el resto estando parado. La
escalabilidad va de construir carreteras ms anchas,
no coches ms rpidos.
Steve Swartz
1. Anatoma de un Proceso
Un proceso cualquiera, no slo los que son propios de Erlang, tiene
unas caractersticas especficas que lo distingue, por ejemplo, de un hilo.
Los procesos son unidades de un programa en ejecucin que tienen un
cdigo propio y un espacio de datos propio (normalmente llamado heap).
Se podra decir que un proceso cumple los principios del ser vivo, ya
que puede nacer (crearse), crecer (ampliando sus recursos asignados),
64
Procesos
Nota
Cuando se lanza un proceso, en consola podemos ver su
representacin, en forma de cadena, como <X.Y.Z>. Los valores
que se representan en esta forma equivalen a:
2. Ventajas e inconvenientes
Hemos realizado una introduccin rpida y esquemtica de lo que es
un proceso en general y un proceso Erlang, para dar una visin a alto
nivel del concepto. Como dijimos al principio, los procesos en Erlang
no son los del sistema operativo y, por tanto, tienen sus diferencias,
sus caractersticas especiales y sus ventajas e inconvenientes. En este
65
Procesos
1
No obstante, por mquina virtual lanzada el lmite es algo ms bajo por defecto, con el parmetro +P
se puede configurar un nmero mayor, siendo el valor de procesos mximo por defecto de 32.768, y
pudindose ajustar este valor de 16 a 134.217.727.
66
Procesos
3. Lanzando Procesos
El lanzamiento de los procesos en Erlang se realiza con una construccin
del lenguaje, en concreto una funcin para facilitar su compresin y
uso (ya que es un BIF o funcin interna) llamado spawn/1. Esta funcin
interna se encarga de lanzar un proceso que ejecute el cdigo pasado
como parmetro, junto con la configuracin para lanzar el proceso. El
retorno a esta llamada es el identificador del proceso lanzado.
2
Si almacensemos el identificador de proceso llamado comnmente PID
en una variable veramos que el proceso ya no est activo mediante la
funcin interna is_process_alive/1:
67
Procesos
4. Bautizando Procesos
Otra de las ventajas disponibles en Erlang sobre los procesos, es poder
darles un nombre. Esto facilita mucho la programacin ya que slo
necesitamos conocer el nombre de un proceso para poder acceder a l.
No es necesario que tengamos el identficador que se ha generado en un
momento dado para ese proceso.
> receive
> Dato -> io:format("recibido: ~p~n", [Dato]
> end.
68
Procesos
Nota
Cada proceso en Erlang tiene una cola de mensajes que almacena
los mensajes recibidos durante la vida del proceso, para que
cuando se ejecute receive, el mensaje pueda ser desencolado y
procesado.
69
Procesos
Importante
Las secciones de opcin dentro de receive pueden tener tambin
guards. En caso de que el mensaje recibido no concuerde con
ninguna de las opciones dadas ser ignorado y se seguir
manteniendo el proceso en modo de escucha.
-module(escucha).
-compile([export_all]).
escucha() ->
receive
{Desde, Mensaje} ->
70
Procesos
para(Pid) ->
Pid ! stop,
ok.
init() ->
spawn(escucha, escucha, []).
Con este ejemplo queda claro que lanzar un proceso es una actividad
trivial, al igual que el intercambio de mensajes entre procesos. Esta
es la base sobre la que se fundamenta una de las aplicaciones
ms importantes de Erlang, la solucin de problemas en entornos
concurrentes. Tambin es la base de la mayora de cdigo que se escribe
71
Procesos
6. Procesos Enlazados
Otra de las funcionalidades que proporciona Erlang respecto a los
procesos es la capacidad para enlazarlos funcionalmente. Es posible
establecer una vinculacin o enlace vital entre procesos de modo que si
a cualquiera de ellos le sucede algo, el otro es inmediatamente finalizado
por el sistema.
Nota
La consola est diseada para procesar las excepciones, por lo
que una vinculacin de error con la misma no provoca su cierre
por el error recibido, sino que simplemente indica que ha recibido
una excepcin de salida.
72
Procesos
-module(gemelos).
-compile([export_all]).
lanza() ->
spawn(gemelos, crea, []),
ok.
crea() ->
spawn_link(gemelos, zipi, [0]),
timer:sleep(500),
zape(0).
zipi(A) ->
io:format("zipi - ~w~n", [A]),
timer:sleep(1000),
zipi(A+1).
zape(A) ->
io:format("zape - ~w~n", [A]),
timer:sleep(1000),
case A of
A when A < 5 -> ok
end,
zape(A+1).
> gemelos:lanza().
zipi - 0
ok
zape - 0
zipi - 1
zape - 1
zipi - 2
zape - 2
zipi - 3
zape - 3
zipi - 4
zape - 4
zipi - 5
zape - 5
zipi - 6
>
73
Procesos
Analizando la salida, vemos que se imprime zape por pantalla hasta que
al evaluar el cdigo se produce un error que termina ese proceso y su
enlace, es decir, el proceso zipi.
-module(lanzador).
-compile([export_all]).
init() ->
spawn(lanzador, loop, []).
loop() ->
receive
{link, Pid} ->
link(Pid);
error ->
throw(error)
end,
loop().
-module(gemelos_lanzador).
-compile([export_all]).
lanza() ->
LanzadorPid = lanzador:init(),
Zipi = spawn(gemelos, zipi, [0]),
lanzador:agrega(LanzadorPid, Zipi),
timer:sleep(500),
Zape = spawn(gemelos, zape, [0]),
lanzador:agrega(LanzadorPid, Zape),
LanzadorPid.
zipi(A) ->
io:format("zipi - ~w~n", [A]),
timer:sleep(1000),
74
Procesos
zipi(A+1).
zape(A) ->
io:format("zape - ~w~n", [A]),
timer:sleep(1000),
zape(A+1).
Importante
Para que la finalizacin de un proceso provoque que todos
sus enlaces tambin finalicen, debe producirse una finalizacin
por error. Si un proceso finaliza su ejecucin de forma normal
y satisfactoria, queda finalizado y desenlazado del resto de
procesos pero los dems no finalizan. En otras palabras, para
que un proceso enlazado sea finalizado por otro, el proceso que
provoca la cada de los procesos en cascada debe de haber
acabado con un error de ejecucin.
7. Monitorizacin de Procesos
En contraposicin al enlace vital, el enlace informativo o monitorizacin
tal y como se conoce en Erlang, permite recibir el estado de cada proceso
como mensaje. Este mecanismo permite que podamos conocer si un
proceso sigue activo o si ha finalizado su ejecucin, ya sea por un error
o de forma normal. Este tipo de enlace es diferente al anterior que
simplemente propaga los errores haciendo que se produzcan en todos
los procesos enlazados.
75
Procesos
El uso de monitores nos puede servir para crear un lanzador como el del
apartado anterior pero que, al morir un proceso, sea capaz de relanzarlo
cuando se recibe la notificacin de terminacin. Se trata de un monitor
que se puede implementar de la siguiente forma:
-module(monitor).
-export([init/0, agrega/2]).
init() ->
Pid = spawn(fun() -> loop([]) end),
register(monitor, Pid),
ok.
loop(State) ->
receive
{monitor, From, Name, Fun} ->
76
Procesos
> monitor:init().
ok
> monitor:agrega(hola_mundo, fun() ->
> receive
> Any ->
> io:format("Hola ~s!~n", [Any])
> end
> end).
hola_mundo
> hola_mundo ! "Manuel".
Hola Manuel!
"Manuel"
reavivando hijo en <0.38.0>
> hola_mundo ! "Miguel".
Hola Miguel!
"Miguel"
reavivando hijo en <0.40.0>
8. Recarga de cdigo
Uno de los requisitos con los que se desarroll la mquina virtual de
Erlang fue que el cdigo pudiese cambiar en caliente sin afectar su
funcionamiento. El mecanismo para cambiar el cdigo es parecido al que
se realiza con los lenguajes de scripting con algunos matices.
77
Procesos
-module(prueba).
-export([code_change/0, init/0]).
init() ->
loop().
code_change() ->
loop().
loop() ->
receive Any -> io:format("original: ~p~n", [Any]) end,
prueba:code_change().
> c(prueba).
{ok,prueba}
> Pid = spawn(prueba, code_change, []).
<0.39.0>
> Pid ! "hola", ok.
original: "hola"
ok
Importante
Erlang puede mantener hasta dos instancias de cdigo en
ejecucin. Si tenemos un cdigo ejecutndose que no se llama
de forma full qualified, aunque cambiemos el cdigo BEAM no
se recargar. Pero si se lanza otro proceso nuevo, se har con la
nueva versin del cdigo. En ese momento habr dos instancias
diferentes de un mismo cdigo. Si se volviese a modificar el
cdigo, el sistema debe de extinguir la versin ms antigua del
cdigo para quedarse slo con las dos ltimas, por lo que los
procesos antiguos con el cdigo ms antiguo seran eliminados.
78
Procesos
loop() ->
receive Any -> io:format("cambio: ~p~n", [Any]) end,
prueba:code_change().
> c(prueba).
{ok,prueba}
> Pid ! "hola", ok.
original: "hola"
ok
> Pid ! "hola", ok.
cambio: "hola"
ok
-module(prueba).
-export([code_change/0]).
code_change() ->
loop().
loop() ->
receive
update ->
code:purge(?MODULE),
code:load_file(?MODULE),
?MODULE:code_change();
Any ->
io:format("original: ~p~n", [Any]),
loop()
end.
Una vez hecho esto podemos provocar la recarga del cdig enviando el
mensaje update desde consola fcilmente:
79
Procesos
Esta vez la llamada update nos ahorra el tener que hacer otra llamada
adicional para que se ejecute el cdigo nuevo.
9. Gestin de Procesos
Como hemos dicho desde el principio, Erlang ejecuta su cdigo dentro
de una mquina virtual, por lo que posee su propia gestin de procesos,
de la que ya comentamos sus ventajas e inconvenientes.
exit(Pid, Reason).
80
Procesos
[node()|nodes()]
(test1@bosqueviejo)> nodes().
[]
(test1@bosqueviejo)> Remoto = test2@bosqueviejo,
(test1@bosqueviejo)> net_kernel:connect_node(Remoto).
true
(test1@bosqueviejo)> nodes().
[test2@bosqueviejo]
81
Procesos
-module(hash).
-export([init/1, get/2, set/3]).
init(Node) ->
io:format("iniciado~n"),
spawn(Node, fun() ->
loop([{"hi", "hola"}, {"bye", "adios"}])
end).
loop(Data) ->
receive
{get, From, Key} ->
Val = proplists:get_value(Key, Data),
From ! Val,
loop(Data);
{set, Key, Value} ->
loop([{Key, Value}|Data]);
stop ->
ok
end.
82
Procesos
El lanzamiento del proceso se realiza como hasta ahora, lo nico que vara
es la forma en la que se registra su nombre. Debe usarse el mdulo global
con global:register_name/2. El acceso a un proceso as registrado
se realiza como hasta ahora, a travs del nombre. La accesibilidad existe
desde cualquier nodo que est conectado con el que posee el proceso.
Nota
Hay muchos casos en los que el mdulo global puede tener un
rendimiento bastante bajo, o incluso hasta defectuoso. Por esta
4
razn han aparecido sustitutos como gproc (que requiere del
parcheo de parte del cdigo OTP de Erlang), o mdulos que no
5
requieren de ninguna modificacin en la base como nprocreg .
4
https://github.com/uwiger/gproc
5
https://github.com/nitrogen/nprocreg
83
Procesos
84
Procesos
Nota
A travs de multicall/3 en lugar de call/4, del mdulo rpc
podemos envar el cdigo a cada uno de los nodos conectados
en el cluster.
Estos datos pueden ser manejados a travs del uso de las siguientes
funciones internas:
get/0, get/1
Cuando se indica sin parmetros se obtienen todos los datos
contenidos dentro de ese proceso. El formato de esta devolucin es
una lista de propiedades que puede ser manejada con las funciones
del mdulo proplists.
get_keys/1
Se emplea para obtener todas las claves cuyos valores son los
indicados como nico parmetro de la llamada a la funcin.
put/2
Almacena el par clave-valor pasados como parmetros, siendo el
primero la clave y el segundo el valor.
erase/0, erase/1
Sin parmetros se encarga de eliminar todas las ocurrencias del
diccionario. Es muy til para limpiar completamente el diccionario.
Con el parmetro clave, se encarga nicamente de eliminar el valor
correspondiente a esa clave.
85
Captulo 6. ETS, DETS y Ficheros
Escribir es recordar, pero leer tambin es recordar.
Franois Mauriac
1. ETS
Las siglas ETS se refieren a Erlang Term Storage, o almacenaje de trminos
de Erlang. Los trminos ya los habamos revisado anteriormente, por lo
que sabemos que se trata de tuplas, en las que el primer elemento de la
tupla acta como clave.
La razn para crear las tablas ETS fue la de poder almacenar gran cantidad
de datos con un tiempo de acceso siempre constante, ya que en los
lenguajes funcionales el tiempo de acceso a la informacin suele ser
funcin logartmica. Otro motivo fue el proveer al desarrollador de un
modo de extraer, almacenar y tratar la informacin con los mecanismos
1
propios del lenguaje . Adems, para que el uso de este sistema fuese
ms rpido, las funciones para manejar las funcionalidades de ets se
encuentran en formato de BIF.
86
ETS, DETS y Ficheros
Conjunto (set)
Es el tipo por defecto. A semejanza de los conjuntos como concepto
matemtico, cada elemento (cada clave de cada tupla) debe de ser
nico a la hora de realizar la insercin dentro del conjunto. El orden
interno de los elementos no est definido.
Bolsa (bag)
La bolsa elimina la restriccin de que el primer elemento ya exista,
pero mantiene la propiedad de que las tuplas, comparadas en su
conjunto con otras, deben de ser distintas.
private
Crea la ETS de mbito privado. Esto quiere decir que no permite a
ningn otro proceso el acceso a la misma.
87
ETS, DETS y Ficheros
protected
El mbito protegido para la ETS garantiza el acceso de lectura a
todos los procesos que conozcan el identificador de la ETS, pero
slo permite la escritura para el proceso que la cre.
public
Garantiza el acceso a todos los procesos, tanto para lectura como
escritura, a la ETS a travs del identificador de la misma.
Nota
Una ETS mantiene, a nivel de concurrencia, siempre los
parmetros de aislamiento y atomicidad ntegros, por lo que
asegura que una operacin de escritura sobre un objeto de
una ETS, en caso de que sea correcta o falle lo har de forma
completa (atomicidad). Cualquier operacin de lectura slo podr
ver el conjunto final de las modificaciones en caso de xito
(aislamiento).
Tipo de tabla
Se debe de especificar alguno de los tipos de ETS vistos: set,
ordered_set, bag o duplicate_bag.
Acceso a la tabla
Se debe de especificar alguno de los tipos de accesos para la ETS
vistos: public, protected o private.
named_table
Si se especifica esta opcin, el primer parmetro de la funcin es
empleado como identificador para poder acceder a la tabla.
keypos
En caso de que queramos que la clave de la ETS no sea el primer
elemento de la tupla podemos agregar esta opcin de la forma:
{keypos, Pos}
88
ETS, DETS y Ficheros
heir
El sistema puede establecer un proceso hijo al que pasarle el control
de la ETS, de modo que si algo le sucediese al proceso que cre la
ETS, el proceso hijo recibira el mensaje:
Concurrencia
Por defecto, las ETS mantienen un nivel de concurrencia por
bloqueo completo, es decir, mientras se est trabajando con la tabla
ningn otro proceso puede acceder a ella, ya sea para leer o escribir.
No obstante, a travs de la opcin:
{read_concurrency, true}
{write_concurrency, true}
compressed
Los datos de la ETS se comprimen para almacenarse en memoria. Al
trabajar sobre datos comprimidos los procesos de bsqueda y toma
89
ETS, DETS y Ficheros
Importante
Ante el uso de dos opciones que colisionen, como el hecho de
emplear conjuntamente la opcin bag y la opcin set, el sistema
emplear la ltima leda de la lista de opciones. Por ejemplo, en
este caso:
90
ETS, DETS y Ficheros
-module(ets_show).
-compile([export_all]).
show_all(Ets) ->
show_all(Ets, ets:first(Ets), []).
main() ->
ets:new(bolsa, [named_table, bag]),
Colores = [{rojo,255,0,0},{verde,0,255,0},{azul,0,0,255}],
ets:insert(bolsa, Colores),
show_all(bolsa).
91
ETS, DETS y Ficheros
Para esto se definen dos tomos que tienen una semntica especial para
2
el gestor de las ETS. Son las llamadas variables sin importancia y el
comodn. Las variables sin importancia se pueden especificar como '$0',
'$1', '$2', ...; La numeracin slo es relevante en caso de especificar la
forma en la que se obtendrn los resultados (para las funciones como
select/2).
'$1'
{'$1',255,'_','_'}
Nota
Si empleamos la funcin match_object/2 en lugar de match/2
se retornar siempre el objeto completo. La concordancia se
tendr en cuenta slo a nivel de eleccin y no a la hora de
organizar los datos para su devolucin.
Por ltimo, vamos a ver el uso de la funcin select/2, como una funcin
ms avanzada que nos da la posibilidad, no slo de enviar una tupla
de concordancia, sino tambin una parte de guardas y la proyeccin (el
cmo se visualizarn en el resultado). Esta funcin nos da para las ETS la
2
El nombre de variable sin importancia es una traduccin prestada del ingls don't care al que hace
referencia el sitio Learn You Some Erlang [http://learnyousomeerlang.com/].
92
ETS, DETS y Ficheros
misma potencia que nos brindan las listas de compresin sobre las listas
que ya vimos en Seccin1.4, Listas del Captulo2, El lenguaje.
{
{'$0','$1','$2','$3'},
[{'<','$1',0}],
['$0']
}
He separado en cada lnea cada uno de los tres parmetros que se deben
enviar para cumplir con la especificacin de concordancia. En la primera
lnea se puede ver que no se ha realizado ninguna primera criba, sino
que se aceptan todas las ETS que tengan ese nmero de tuplas.
{
{'$1','$2','$4','$8'},
[{'andalso',
{'==','$2',0},
{'==','$8',0}
}],
[ '$$' ]
}
ets:delete(bolsa, verde)
93
ETS, DETS y Ficheros
2. DETS
Las DETS son ETS que se almacenan en disco (Disk Erlang Term Storage).
Al tratarse tambin de almacenaje de trminos, poseen un interfaz
al programador muy parecido al de las ETS. El medio de tratamiento
y almacenamiento de la informacin es distinto. Las DETS pueden
mantener persistencia de informacin mientras que las ETS no la tienen.
94
ETS, DETS y Ficheros
Conjunto (set)
Este es el tipo por defecto. A semejanza de los conjuntos como
concepto matemtico, cada elemento (cada clave de cada tupla)
debe de ser nica a la hora de realizar la insercin del elemento
dentro del conjunto. El orden interno no est definido.
Bolsa (bag)
La bolsa elimina la restriccin de que el identificador de la tupla sea
igual, pero mantiene la propiedad de que las tuplas, comparadas en
su conjunto con otras, deben de ser distintas.
Nota
A da de hoy (en la revisin R15 de Erlang/OTP), no existe librera
que permita escribir de forma ordenada los trminos hacia disco.
Est pendiente y posiblemente en futuras liberaciones veamos
que finalmente se agrega a esta lista el conjunto ordenado.
3
En este libro no se tratar Mnesia, porque sino el texto se nos extendera unas decenas de pginas ms
y no conseguiramos abarcarlo como se merece.
95
ETS, DETS y Ficheros
Las opciones que se pueden agregar como segundo parmetro son las
siguientes:
{access, Access}
Como acceso son vlidos los valores read o read_write, siendo este
ltimo el que se toma por defecto.
{auto_save, AutoSave}
Se indica un valor entero que indica el intervalo de autoguardado de
la DETS. Es decir, el tiempo en el que se realiza una sincronizacin
entre lo que se mantiene en memoria y el disco. Si se especifica
infinity, el autoguardado es deshabilitado. La opcin por defecto es
180000 (3 minutos).
{min_no_slots, Slots}
Es un ajuste de rendimiento que permite especificar en la creacin
de la tabla el nmero de claves estimado que sern almacenadas.
El valor por defecto es 256.
{max_no_slots, Slots}
El nmero mximo de slots que ser usado. El valor por defecto y
mximo permitido es de 32000000.
{keypos, Pos}
La posicin dentro del trmino en el que se encontrar la clave de
la tupla.
{file, File}
El nombre del fichero que se usar. Por defecto se toma el nombre
de la DETS para la escritura del fichero.
96
ETS, DETS y Ficheros
{ram_file, boolean()}
Si la DETS se mantendr en memoria. Esto quiere decir que la
DETS se copia ntegramente a la memoria al momento de abrirla
realizando el volcado a disco cuando se cierra. Por defecto esta
caracterstica no est activa (false).
{type, Tipo}
El tipo de la tabla, tal y como vimos en la seccin anterior.
Importante
Para no perder informacin, es importante que siempre cerremos
la DETS de forma apropiada, a travs de la funcin close/1. Si
no lo hacemos, al volver a ejecutar el programa, es seguro que
se requerir una reparacin del fichero e incluso podran llegar a
perderse datos.
Al igual que con las ETS, las funciones info/1 e info/2, nos
proporcionan informacin sobre la DETS abierta dado su nombre.
97
ETS, DETS y Ficheros
Importante
Si se intenta abrir el mismo fichero desde dos partes diferentes
del cdigo con los mismos parmetros, el fichero es abierto sin
problemas pero slo la primera vez, el segundo usa esta primera
instancia. Si alguna de las partes cerrase el fichero, como la
instancia tiene reflejado dos usos, se mantiene abierta hasta que
la otra parte tambin cierre el fichero.
Con el uso de esta funcin los datos de la DETS son volcados en la ETS.
Cabe destacar que es necesario haber abierto la DETS con anterioridad.
Los datos que contenga la ETS previamente no se eliminan, a menos que
colisionen con los que vienen de la DETS en cuyo caso se sobreescribirn.
98
ETS, DETS y Ficheros
Nota
En ambos casos, el orden en el que se guardan los elementos es
indeterminado, tanto de DETS a ETS, como en el caso opuesto.
3. Ficheros
En este apartado revisaremos lo que se puede hacer desde Erlang con
los ficheros. Para ello, vamos a diferenciar el tratamiento de los ficheros
entre los dos tipos existentes: binarios y de texto.
Los ficheros que podemos abrir o crear son ficheros que pueden
contener texto, imgenes, audio, vdeo, documentos formateados como
los RTF, o ficheros binarios de cualquier otro tipo.
99
ETS, DETS y Ficheros
raw
Abre el fichero mucho ms rpido, ya que ningn proceso de Erlang
se encarga de manejar el fichero. Sin embargo, este modo de trabajo
tiene limitaciones, como que las funciones del mdulo io no pueden
ser empleadas o que slo el proceso que haya abierto el fichero
puede utilizarlo.
binary
Las operaciones de lectura retornarn listas binarias en lugar de
listas.
{read_ahead, Size}
Activa el buffer de lectura para las operaciones de lectura que son
inferiores al tamao definido en Size. Igual que en el caso anterior,
decrementa el nmero de llamadas al sistema para acceso a disco,
por lo que aumenta el rendimiento.
compressed
Crea o abre ficheros comprimidos con gzip. Esta opcin puede
combinarse con read o write, pero no ambas.
{encoding, Encoding}
Realiza la conversin automtica de caracteres para y desde un
tipo especfico. La codificacin por defecto es latin1. Las tablas
de codificacin permitidas se pueden revisar en la documentacin
4
oficial de la funcin open/2 .
5
Un ejemplo de apertura de un fichero tan famoso como /etc/
debian_version, para lectura o escritura y el resultado obtenido:
4
http://www.erlang.org/doc/man/file.html#open-2
5
Para los que usan Debian o Ubuntu, o alguna distribucin derivada de estas, es frecuente encontrar el
fichero /etc/debian_version en el sistema de ficheros.
100
ETS, DETS y Ficheros
Nota
Vemos que el retorno de la primera operacin que se realiza
correctamente, nos devuelve un PID. Al no haber empleado la
opcin raw se crea un proceso Erlang intermedio que se encarga
de la informacin del fichero y de realizar los accesos de lectura
y escritura.
Importante
Es importante que cerremos todos los ficheros que abramos ya
que esto repercute, no slo en un uso innecesario de los recursos
6
de los descriptores de ficheros , sino tambin de procesos, ya que
cada fichero abierto de un modo no raw lleva asociado un proceso
Erlang.
101
ETS, DETS y Ficheros
> file:read_file("/etc/debian_version").
{ok,<<"wheezy/sid\n">>}
Nota
Al igual que hemos empleado read_line/1, podemos emplear
io:get_line/2 para realizar la lectura, pasando como primer
parmetro el identificador del fichero abierto.
{nombre, "Manuel"}.
{apellido1, "Rubio"}.
{apellido2, "Jimenez"}.
> file:consult("datos_personales.cfg").
102
ETS, DETS y Ficheros
{ok,[{nombre,"Manuel"},
{apellido1,"Rubio"},
{apellido2,"Jimenez"}]}
103
ETS, DETS y Ficheros
Los bytes ledos de nuestro fichero binario imagen se muestran como una
lista binaria de enteros. En concreto hemos ledo 16 bytes del principio
del archivo PNG. Podemos leer 8 bytes que conforman la firma del PNG
y los 8 que contienen la cabecera del PNG en la siguiente forma (como
7
podemos ver en la wikipedia ):
104
ETS, DETS y Ficheros
file:open("file.bin", [read,write,binary])
Para modificar una parte especfica del fichero, habr que desplazar el
puntero al punto exacto donde queremos escribir. Esto es lo que se
conoce como escrituras y/o lecturas aleatorias (o no secuenciales).
105
ETS, DETS y Ficheros
[
{{bof, 0}, <<137,"PNG">>},
{{eof, -24}, <<0,0,0,0>>},
{{bof, 12}, <<"IHDR">>}
]
106
ETS, DETS y Ficheros
[
{{bof,0}, 4},
{{eof, -24}, 4},
{{bof, 12}, 4}
]
Importante
En caso de emplear listas de caracteres, hay que tener especial
cuidado con los caracteres de UTF-8, ya que algunos emplean
dos bytes para su almacenaje en lugar de slo uno y esto
puede provocar que el cmputo de la posicin sea errneo (o
susceptible de errores).
4. Gestin de Ficheros
Adems de todo lo visto anteriormente para la creacin, modificacin
y lectura de un fichero, podemos realizar ms acciones an con
estos ficheros, como puede ser: renombrarlos, cambiar sus permisos,
8
propietario , copiar el fichero, truncarlo o eliminarlo.
8
El cambio de permisos y propietario depende de cada sistema operativo y los permisos en s que tenga
el usuario que lanz la ejecucin del programa.
107
ETS, DETS y Ficheros
".png"
> filename:absname("logo.png").
"/home/bombadil/logo.png"
file:rename("logo.png", "/tmp/logo.png")
Nota
Las operaciones se realizan sobre ficheros especficos, no sobre
grupos de ficheros como los comandos de consola de los sistemas
operativos, por lo que el uso de comodines como asterisco (*) o
interrogante (?) no se tienen en cuenta como tal, sino que son
interpretados como parte del nombre del fichero.
108
ETS, DETS y Ficheros
file:change_mode("logo.png", 8#00644)
Hay una funcin que engloba todas las funciones del mdulo file
para la gestin de usuarios, grupos y permisos y permite realizar
9
En los sistemas de tipo Unix este dato se puede ver en /etc/passwd donde hay una correspondencia
entre el nombre del usuario y su UID.
109
ETS, DETS y Ficheros
read_file_info/1
Permite leer las propiedades de un fichero retornando un registro
en el que aparecen datos como la fecha y hora de creacin, fecha y
hora de modificacin y fecha y hora del ltimo acceso, adems de
los permisos, tipo de fichero y tamao del mismo. Por ejemplo:
> file:read_file_info("logo.png",size).
{ok,#file_info{size=1718,type=regular,
access=read_write,
atime={{2012,7,18},{14,23,1}},
mtime={{2012,7,18},{14,23,1}},
ctime={{2012,7,18},{14,23,1}},
mode=33188,links=1,major_device=2049,
minor_device=0,
inode=11150880,uid=1000,gid=1000}}
write_file_info/2
Permite modificar cualquiera de los datos del fichero, para ello,
se debe de especificar, como segundo parmetro, un registro de
tipo file_info y rellenarlo con los datos del fichero que deseemos
modificar.
5. Gestin de Directorios
Hasta el momento hemos visto como trabajar con ficheros, su contenido
ya sea de tipo texto o de tipo binario, as como la gestin propia de
los ficheros (copia, renombrado, eliminacin, ...), ahora vamos a tratar la
gestin de los directorios.
110
ETS, DETS y Ficheros
Dir = file:get_cwd(),
file:set_cwd("/miruta"),
%% ejecuta_codigo...
file:set_cwd(Dir).
file:make_dir("/home/bombadil/logos")
mkdir -p /miruta/nuevo1/nuevo2/midir
Con lo que no slo se crea un directorio, sino todo los necesarios hasta
llegar al ltimo indicado en la ruta pasada como parmetro. Esto no lo
realiza make_dir/1. Esta funcin debe de recibir una ruta existente y
111
ETS, DETS y Ficheros
> file:make_dir("/tmp/prueba/dir1").
{error,enoent}
> filelib:ensure_dir("/tmp/prueba/dir1/").
ok
Importante
El parmetro de ensure_dir/1 debe de terminar en barra para
que cree hasta el ltimo directorio, ya que la funcin est creada
con la idea de que se pueda pasar como parmetro la ruta de un
fichero (con el nombre del fichero includo) y cree el directorio
para albergar al fichero:
filelib:ensure_dir("/tmp/midir/logo.png")
5.3. Es un fichero?
Para emplear en las guardas (o guards), al igual que disponemos de las
funciones is_list/1, podemos hacer uso de las funciones del mdulo
filelib: is_dir/1 o is_file/1.
Esto nos permite realizar funciones que nos permitan realizar un proceso
previo de validacin, por ejemplo, en caso de las configuraciones en las
que se nos proporciona una ruta:
112
ETS, DETS y Ficheros
> file:list_dir("/home").
{ok,["bombadil","bayadeoro"]}
Otra forma de obtener los ficheros que nos interesan que podemos
encontrar dentro del mdulo filelib es a travs de la funcin
wildcard/1, la cual nos permite, no slo poner una ruta, sino adems
emplear los comodines para obtener los ficheros que concuerden:
> filelib:wildcard("/home/bombadil/*.png").
["/home/bombadil/logo.png"]
113
Captulo 7. Comunicaciones
No hay lugares remotos. En virtud de los medios de
comunicacin actuales, todo es ahora.
Herbert Marshall Mcluhan
Nivel fsico
En este nivel se encuentran las conexiones fsicas y sus protocolos
especficos, segn la tecnologa en uso: Ethernet, 802.11, Fibre
Channel, etc. A travs de los drivers (o mdulos del kernel) estos
protocolos son transparentes para las aplicaciones. El nivel fsico
siempre se emplea punto a punto, cada mquina se conecta a travs
de un cable o de forma inalmbrica con otra y establecen una
comunicacin uno a uno.
Nivel de red
Es el nivel en el que se establece la base de la red, la identificacin
de los sistemas y el transporte hacia los mismos. En este nivel y en el
alcance que nos hemos propuesto para este libro, slo nos importa
el protocolo IP. Este protocolo proporciona una direccin dentro de
una red y permite establecer una comunicacin a travs de diversos
dispositivos hasta encontrar la direccin de la mquina que debe
recibir el mensaje.
Nivel de transporte
Este nivel es el que establece la forma de conexin entre las
mquinas, la forma en la que se envan y trocean los paquetes para
114
Comunicaciones
Nivel de aplicacin
Este es el nivel ms alto que podemos encontrar en comunicacin.
Aqu se definen y usan protocolos como: HTTP, FTP, SMTP, POP3,
IMAP, etc.
1.1. Direcciones IP
Una direccin IP se representa mediante un nmero binario de 32 bits
(segn IPv4). Las direcciones IP se pueden expresar como nmeros de
notacin decimal: se dividen los 32 bits de la direccin en cuatro octetos.
1
El valor decimal de cada octeto puede estar entre 0 y 255 .
Importante
Las direcciones IP en Erlang se emplean a travs de un formato de
tupla formada por cuatro elementos enteros. Esta forma es en la
que generalmente trabaja el mdulo inet que es el que se encarga
de las comunicaciones, tanto para conexiones cliente, como para
servidor:
{127,0,0,1}
115
Comunicaciones
La direccin que tiene a uno todos los bits de su parte de equipo sirve
para comunicar con todos los equipos de la red en la que se ubica. Se
denomina direccin de broadcast.
116
Comunicaciones
pblica, de manera que las direcciones privadas son ideales para ellos.
Las direcciones privadas tambin se pueden utilizar en una red en la que
no hay suficientes direcciones pblicas disponibles.
1.2. Puertos
Los puertos de comunicaciones son la base sobre la que se sustentan
los protocolos de transporte TCP y UDP. Estos protocolos establecen
conexiones salientes y entrantes en puertos denominados activos o
pasivos respectivamente.
Nota
En la mayora de sistemas operativos existe un fichero de texto
plano denominado services que contiene, formateado en dos
columnas: el nombre de servicio y el puerto que emplea dicho
servicio. La columna del puerto, adems, viene formateada de
forma que se indica el nmero, una barra inclinada y el tipo de
transporte que se emplea:
http 80/tcp
http 80/udp
ftp-data 20/tcp
ftp 21/tcp
domain 53/tcp
domain 53/udp
3
Esto es debido tambin a que la mayora de servicios que se prestan estn en este rango, de modo que
en un servidor un usuario sin privilegios no pueda establecer un puerto pasivo en el puerto 80 (dedicado
a HTTP), 25 (de SMTP), 22 (de SSH) o 21 (de FTP) entre otros.
117
Comunicaciones
Nota
En la jerga de los sistemas de comunicacin existen algunas
palabras clave que se emplean para determinar ciertos aspectos
de la comunicacin o los elementos que la componen. La palabra
anglosajona socket (traducida como zcalo o conector) es la
palabra que se suele emplear para indicar una conexin. Cuando
se establece un puerto pasivo mediante TCP se suele decir que el
servidor escucha mientras que si es mediante UDP se dice que el
servidor est enlazado a ese puerto.
TCP
Es orientado a conexin. Requiere que el cliente formalice la
conexin con el servidor y esta se mantiene hasta que una de las
dos partes solicita la desconexin o durante un envo se produzca
un tiempo de espera agotado.
UDP
UDP es un protocolo de datagramas. Un datagrama puede ser
enviado hacia un servidor pero no se comprueba el estado de
recepcin. No se espera respuesta del mismo. El tratamiento de este
tipo de paquetes carga menos la red y los sistemas operativos pero
si la red no es fiable puede existir prdida de informacin.
118
Comunicaciones
list | binary
Indica si se quiere recibir el paquete como una lista o un binario. El
valor por defecto es list.
inet | inet6
Emplea IPv4 o IPv6 para las conexiones. El valor por defecto es inet.
119
Comunicaciones
Nota
Hay disponibles muchas opciones ms a las que no entraremos ya
que son conceptos ms avanzados o muy especficos, con lo que
salen del mbito de explicacin de este captulo. Si desea ms
informacin sobre la funcin gen_udp:open/2 puede revisar la
siguiente direccin:
http://www.erlang.org/doc/man/gen_udp.html#open-2
-module(udpsrv).
-export([start/1, init/1, loop/1]).
start(Port) ->
spawn(?MODULE, init, [Port]),
ok.
init(Port) ->
{ok, Socket} = gen_udp:open(Port),
loop(Socket).
loop(Socket) ->
receive
stop ->
gen_udp:close(Socket);
Packet when is_record(Packet, udp) ->
io:format("recibido(~p): ~p~n", [
Packet#udp.ip, Packet#udp.msg
]),
#udp{ip=IP,port=Port} = Packet,
gen_udp:send(Socket, IP, Port, "recibido"),
loop(Socket)
end.
En el ejemplo hemos usado un registro formado por los cuatro datos que
nos enva cada paquete UDP adems del identificador:
120
Comunicaciones
Socket
El manejador retornado por la funcin open/1 u open/2.
IP
La direccin IP en formato de tupla.
InPortNo
El puerto origen del paquete recibido.
Packet
El paquete recibido.
Podemos abrir una consola de Erlang y lanzar el servidor. Para saber que
el puerto se encuentra enlazado emplearemos la funcin inet:i/0 que
nos proporciona informacin sobre las comunicaciones:
> udpsrv:start(2020).
ok
> inet:i().
Port [...] Recv Sent Owner Local Address [...] State Type
593 [...] 0 0 <0.34.0> *:2020 [...] BOUND DGRAM
ok
La salida nos muestra el proceso (Owner) que tiene enlazado (State =:=
BOUND) el puerto 2020 (Local) de tipo UDP (Type =:= DGRAM). Nos
proporciona tambin otros datos estadsticos como los bytes recibidos
(Recv) y enviados (Sent).
> inet:i().
Port [...] Recv Sent Owner Local Address [...] State Type
593 [...] 11 0 <0.34.0> *:2020 [...] BOUND DGRAM
121
Comunicaciones
Vamos a modificar el cdigo del servidor para que retorne al cliente una
cadena de texto:
-module(udpsrv).
-export([start/1, init/1, loop/1]).
start(Port) ->
spawn(?MODULE, init, [Port]),
ok.
init(Port) ->
{ok, Socket} = gen_udp:open(Port),
loop(Socket).
loop(Socket) ->
receive
stop ->
gen_udp:close(Socket);
Packet when is_record(Packet, udp) ->
io:format("recibido(~p): ~p~n", [
Packet#udp.ip, Packet#udp.msg
]),
#udp{ip=IP,port=Port} = Packet,
gen_udp:send(Socket, IP, Port, "recibido"),
loop(Socket)
end.
> udpsrv:start(2020).
ok
> {ok, Socket} = gen_udp:open(0, [{active, false}]).
{ok,#Port<0.599>}
> gen_udp:send(Socket, {127,0,0,1}, 2020, "hola mundo!").
ok
recibido({127,0,0,1}): "hola mundo!"
> gen_udp:recv(Socket, 1024).
{ok,{{127,0,0,1},2020,"recibido"}}
122
Comunicaciones
list | binary
Indica si se quiere recibir el paquete como una lista o un binario. El
valor por defecto es list.
inet | inet6
Emplea IPv4 o IPv6 para las conexiones. El valor por defecto es inet.
Nota
Hay disponibles muchas opciones ms a las que no entraremos ya
que son conceptos ms avanzados o muy especficos, con lo que
salen del mbito de explicacin de este captulo. Si desea ms
informacin sobre la funcin gen_tcp:listen/2 puede revisar
4
este enlace
4
http://www.erlang.org/doc/man/gen_tcp.html#listen-2
123
Comunicaciones
124
Comunicaciones
Importante
El socket que se estableci para escucha se puede igualmente
cerrar con close/1. Conviene cerrar los puertos antes de finalizar
la ejecucin de los servidores para liberar los puertos empleados.
125
Comunicaciones
Para que el nuevo socket generado sepa que tiene que enviar
sus paquetes al nuevo proceso hay que emplear la funcin
controlling_process/2, que tiene la forma:
start(Port) ->
spawn(fun() -> srv_init(Port) end).
srv_init(Port) ->
Opts = [{reuseaddr, true}, {active, false}],
{ok, Socket} = gen_tcp:listen(Port, Opts),
srv_loop(Socket).
srv_loop(Socket) ->
{ok, SockCli} = gen_tcp:accept(Socket),
Pid = spawn(fun() -> worker_loop(SockCli) end),
gen_tcp:controlling_process(SockCli, Pid),
inet:setopts(SockCli, [{active, true}]),
srv_loop(Socket).
worker_loop(Socket) ->
receive
{tcp, Socket, Msg} ->
io:format("Recibido ~p: ~p~n", [self(), Msg]),
timer:sleep(5000), %% 5 segundos de espera
Salida = io_lib:format("Eco: ~s", [Msg]),
gen_tcp:send(Socket, Salida),
worker_loop(Socket);
{tcp_closed, Socket} ->
io:format("Finalizado.~n");
Any ->
io:format("Mensaje no reconocido: ~p~n", [Any])
end.
126
Comunicaciones
6
La funcin cli_send/2 permite conectarse a un puerto local , enviar
un mensaje y esperar por el retorno antes de finalizar la comunicacin.
cli_concurrent_send(Port) ->
Send = fun(I) ->
Text = io_lib:format("i=~p", [I]),
spawn(tcpcli, cli_send, [Port, Text])
end,
lists:foreach(Send, lists:seq(1,10)).
5. Ventajas de inet
Erlang no slo dispone de funciones para manejar las comunicaciones a
nivel transporte. El mdulo inet a travs de la funcin setopts/2 provee
6
En este ejemplo no hemos empleado direcciones IP, por lo que se emplea por defecto la IP local o
127.0.0.1.
127
Comunicaciones
Nota
La decodificacin la realiza nicamente a nivel de recepcin, el
envo deberemos de componerlo nosotros mismos y enviarlo con
la funcin de send/2 de gen_tcp.
http://localhost:8080/
{ok,#Port<0.605>}
> flush().
Shell got {http,#Port<0.605>,
{http_request,'GET',{abs_path,"/"},{1,1}}}
Shell got {http,#Port<0.605>,
{http_header,14,'Host',undefined,"localhost:8080"}}
[...]
Shell got {http,#Port<0.605>,http_eoh}
ok
> Msg = "HTTP/1.0 200 OK
> Content-length: 1
> Content-type: text/plain
>
> H",
> gen_tcp:send(SC, Msg).
ok
Los mensajes recibidos por el sistema son tuplas que tienen como primer
elemento http. Como en los casos de tcp el segundo parmetro es Socket.
Como tercer parmetro puede aparecer otra tupla cuyo primer parmetro
es:
7
http://www.erlang.org/doc/man/inet.html#setopts-2
128
Comunicaciones
http_request
Si se trata de la primera lnea de peticin. Esta tupla tendr 4
campos: http_request, mtodo HTTP (GET, POST, PUT o DELETE entre
otros), URI y versin HTTP en forma de tupla de dos elementos. Un
ejemplo:
http_header
Las siguientes lneas a la peticin son las lneas de cabecera. Que se
estructuran en una tupla de 5 campos: http_header, bit de cabecera,
nombre de la cabecera, valor reservado (undefined) y valor de la
cabecera.
http_eoh
Este dato se transmite en forma de tomo. Indica que la recepcin
de cabeceras ha finalizado.
-module(httpsrv).
-export([start/1]).
OK").
start(Port) ->
spawn(fun() -> srv_init(Port) end).
srv_init(Port) ->
Opts = [{reuseaddr, true}, {active, false}, {packet, http}],
{ok, Socket} = gen_tcp:listen(Port, Opts),
srv_loop(Socket).
srv_loop(Socket) ->
{ok, SockCli} = gen_tcp:accept(Socket),
Pid = spawn(fun() -> worker_loop(SockCli) end),
gen_tcp:controlling_process(SockCli, Pid),
inet:setopts(SockCli, [{active, true}]),
srv_loop(Socket).
worker_loop(Socket) ->
receive
{http, Socket, http_eoh} ->
inet:setopts(Socket, [{packet, raw}]),
worker_loop(Socket);
{http, Socket, Header} ->
io:format("Recibido ~p: ~p~n", [self(), Header]),
worker_loop(Socket);
{tcp, Socket, Msg} ->
129
Comunicaciones
Nota
Si empleamos el parmetro {active, false} para emplear la funcin
recv/2 en lugar de receive hay que tener presente que el retorno
de la funcin recv/2 ser: {ok, HttpPacket}, mientras que el
retorno de receive ser: {http, Socket, HttpPacket}.
130
Captulo 8. Ecosistema Erlang
La construccin exitosa de toda mquina depende
de la perfeccin de las herramientas empleadas.
Quien sea un maestro en el arte de la fabricacin de
herramientas poseer la clave para la construccin
de todas las mquinas.
Charles Babbage
1. Iniciar un Proyecto
A lo largo de los captulos hemos realizado la mayor parte del cdigo
en la consola de Erlang y vimos la organizacin del cdigo interno y la
realizacin de mdulos. An no hemos comentado la forma que debe
tener nuestro espacio de trabajo, los directorios que es conveniente crear
y la disposicin de los ficheros dentro de estos directorios.
src
Este directorio contendr el cdigo fuente. Todos los ficheros cuya
extensin sea .erl.
ebin
Aqu se almacenarn los ficheros de tipo .beam, es decir la
compilacin de nuestra aplicacin.
1
Basho Technologies es una empresa estadounidense que desarrolla la base de datos Riak.
131
Ecosistema Erlang
include
Los ficheros que se almacenan en este directorio son los de tipo
cabecera .hrl.
priv
Cuando el proyecto requiere de ficheros especficos para funcionar
se introducen en este directorio ficheros como certificados, pginas
HTML, hojas de estilo CSS o cdigos JavaScript entre otros.
Nota
Hay ms directorios por defecto para proyectos Erlang/OTP como
c_src donde se alojan los ficheros de extensin escritos en C,
test para los cdigos de pruebas de EUnit o CommonTest o deps
es donde se bajan otros proyectos de terceros para incluir su
cdigo dentro de nuestro proyecto.
Para esta tarea nos ayudaremos de rebar. Creamos los tres directorios
base y pasamos a instalar rebar.
Ahora solo nos falta copiar el script generado a una ruta visible por
nuestro PATH. Normalmente como super usuario en sistemas de tipo
2
Unix en una ruta como /usr/bin, /usr/local/bin o /opt/local/
bin.
2
Sistemas Unix o tipo Unix como BSD, Linux, MacOS X u OpenSolaris entre otros.
132
Ecosistema Erlang
Nota
La utilidad rebar se encuentra tambin disponible para Windows
3
a travs del repositorio bifurcado (fork) de IRONkyle .
-module(webserver).
-export([start/1]).
start(Port) ->
spawn(fun() -> srv_init(Port) end).
srv_init(Port) ->
Opts = [{reuseaddr, true}, {active, false}, {packet, http}],
{ok, Socket} = gen_tcp:listen(Port, Opts),
srv_loop(Socket).
srv_loop(Socket) ->
{ok, SockCli} = gen_tcp:accept(Socket),
Pid = spawn(fun() -> worker_loop(SockCli, []) end),
gen_tcp:controlling_process(SockCli, Pid),
inet:setopts(SockCli, [{active, true}]),
srv_loop(Socket).
3
https://github.com/IRONkyle/rebar
133
Ecosistema Erlang
-module(fileserver).
-export([send/1]).
">>).
send(Request) ->
"/" ++ Path = proplists:get_value(path, Request, "/"),
{ok, CWD} = file:get_cwd(),
RealPath = filename:join(CWD, Path),
case file:read_file(RealPath) of
{ok, Content} ->
Size = list_to_binary(
io_lib:format("~p", [byte_size(Content)])
),
Type = mimetype(Path),
<<
?RESP_200/binary, Type/binary,
"\nContent-lenght: ", Size/binary,
"\r\n\r\n", Content/binary
>>;
{error, _} ->
?RESP_404
end.
mimetype(File) ->
case filename:extension(string:to_lower(File)) of
".png" -> <<"image/png">>;
".jpg" -> <<"image/jpeg">>;
".jpeg" -> <<"image/jpeg">>;
".zip" -> <<"application/zip">>;
".xml" -> <<"application/xml">>;
".css" -> <<"text/css">>;
".html" -> <<"text/html">>;
".htm" -> <<"text/html">>;
".js" -> <<"application/javascript">>;
".ico" -> <<"image/vnd.microsoft.icon">>;
134
Ecosistema Erlang
_ -> <<"text/plain">>
end.
{application, webserver , [
{description , "Erlang Web Server"},
{vsn , "1.0"},
{applications ,[
kernel, stdlib, inets
]}
]}.
Nota
Como versin en la lnea de vsn podemos emplear las palabras
clave: git, hg, bzr, svn o {cmd, Cmd}. Las primeras indican al sistema
que tome el tag o el nmero de revisin del sistema de control de
versiones. La ltima indica que ejecute el comando contenido en
Cmd para obtener la versin.
4
En la pgina de referencia de app podemos ver una lista ms completa
y detallada de las opciones que permite el fichero para iniciar una
aplicacin.
2. Compilar y Limpiar
Una vez que tenemos el directorio src creado podemos compilarlo todo
ejecutando el comando: rebar compile. El comando rebar se encarga de
crear el directorio ebin y depositar los ficheros beam dentro de l:
$ rebar compile
==> webserver_simple (compile)
Compiled src/webserver.erl
Compiled src/fileserver.erl
4
http://www.erlang.org/doc/man/app.html
135
Ecosistema Erlang
Para ello necesitamos crear otro fichero de cdigo dentro del directorio
src. Llamaremos a este fichero webserver_app.erl y pondremos el
siguiente contenido:
-module(webserver_app).
5
Los comportamientos (behaviours) son un mecanismo de inversin de control (IoC) que posibilita la
creacin de cdigo abstracto ms concreto para el usuario. Estos sern vistos en mayor profundidad en
el Volumen II.
136
Ecosistema Erlang
-behaviour(application).
start() ->
application:start(webserver).
stop(_State) ->
ok.
{application, webserver, [
{description, "Erlang Web Server"},
{vsn, "1.0"},
{applications,[
kernel, stdlib, inets
]},
{mod, {webserver_app, []}}
]}.
4. Dependencias
En Internet existen repositorios con miles de libreras para Erlang.
7 8
Los ms representativos son github.com y bitbucket . En estos sitios
6
Los argumentos usados para la lnea de comandos se pueden revisar en el Apndice B, La lnea de
comandos.
7
https://github.com
8
https://bitbucket.org
137
Ecosistema Erlang
{deps, [
{mimetypes, ".*",
{git, "https://github.com/spawngrid/mimetypes.git",
"master"}
}
]}.
{application, webserver, [
{description, "Erlang Web Server"},
{vsn, "1.0"},
{applications,[
kernel, stdlib, inets, mimetypes
]},
{mod, {webserver_app, []}}
]}.
-module(webserver_app).
-behaviour(application).
start() ->
application:start(mimetypes),
application:start(webserver).
9
https://github.com/spawngrid/mimetypes.git
138
Ecosistema Erlang
stop(_State) ->
ok.
Por ltimo, cambiamos el cdigo escrito para que en lugar de tener el uso
de nuestra funcin mimetype/1 emplee las que provee la librera:
-module(fileserver).
-export([send/1]).
">>).
send(Request) ->
"/" ++ Path = proplists:get_value(path, Request, "/"),
{ok, CWD} = file:get_cwd(),
RealPath = filename:join(CWD, Path),
case file:read_file(RealPath) of
{ok, Content} ->
Size = list_to_binary(
io_lib:format("~p", [byte_size(Content)])
),
[Type] = mimetypes:filename(Path),
<<
?RESP_200/binary, Type/binary,
"\nContent-lenght: ", Size/binary,
"\r\n\r\n", Content/binary
>>;
{error, _} ->
?RESP_404
end.
$ rebar get-deps
==> webserver_deps (get-deps)
Pulling mimetypes from {git,
"https://github.com/spawngrid/mimetypes.git",
"master"}
Cloning into 'mimetypes'...
==> mimetypes (get-deps)
$ rebar compile
==> mimetypes (compile)
Compiled src/mimetypes_scan.xrl
Compiled src/mimetypes_parse.yrl
Compiled src/mimetypes_loader.erl
Compiled src/mimetypes_scan.erl
Compiled src/mimetypes_sup.erl
Compiled src/mimetypes_app.erl
139
Ecosistema Erlang
Compiled src/mimetypes.erl
Compiled src/mimetypes_parse.erl
==> webserver_deps (compile)
Compiled src/webserver_app.erl
Compiled src/webserver.erl
Compiled src/fileserver.erl
Nota
El comando rebar get-deps se emplea para descargar las
dependencias mientras que rebar del-deps se encarga de
eliminarlas. Este ltimo comando es til para realizar una limpieza
del proyecto junto con rebar clean:
$ rebar del-deps
==> mimetypes (delete-deps)
==> webserver (delete-deps)
$ rebar clean
==> webserver (clean)
{deps, [
{mimetypes, ".*",
{git, "https://github.com/spawngrid/mimetypes.git",
"master"}
}
]}.
5. Liberar y Desplegar
El desarrollo de software tiene su culminacin cuando el software puede
ser instalado en sistemas en produccin. Liberar el cdigo consiste en
dejar preparado el producto para su instalacin. Este debe de poderse
empaquetar, construir y lanzar de forma fcil y simple. El despliegue
consiste en el procedimiento de instalacin de este cdigo liberado.
140
Ecosistema Erlang
Importante
El nombre que se d al nodo es preferible que no contenga
guiones bajos. En el proceso de generacin de actualizaciones
(appups y upgrades) podra generar errores.
141
Ecosistema Erlang
{sub_dirs, ["apps/*"]}.
Nota
El directorio apps se emplea cuando se requieren escribir
programas con varias aplicaciones. El comando rebar generate
requiere que esta estructura exista para realizar la liberacin.
{lib_dirs, [Dir1,Dir2..DirN]}
El directorio (o directorios) que contiene las aplicaciones.
Deberemos de agregarlo de la siguiente forma:
{boot_rel, App}
Se pueden crear tantos apartados rel como se necesiten. Uno de
ellos debe marcarse por defecto con esta opcin.
10
http://www.erlang.org/doc/man/reltool.html
142
Ecosistema Erlang
include
Entran todas las aplicaciones menos la que explcitamente se
indique que no entre.
exclude
Entran solo las aplicaciones que se indiquen de forma explcita
que deban de entrar.
derived
Se incluyen las aplicaciones indicadas explcitamente y todas
sus dependencias.
all
Se incluyen todos los mdulos de cada aplicacin includa en
la liberacin.
app
Se incluyen todos los mdulos listados en el fichero .app y
11
derivados .
ebin
Se incluyen todos los mdulos que estn en el directorio ebin
11
de la aplicacin y los derivados .
derived
Se incluyen los mdulos que estn siendo usados por los
includos explcitamente.
none
No se incluye ninguno.
143
Ecosistema Erlang
Nota
Se pueden emplear filtros para agregar ciertos ficheros
slo y segn qu nivel (archivo, sistema o aplicacin). No
profundizaremos en este tema para no extendernos ms y porque
este uso es ms una referencia que el lector puede encontrar
fcilmente en la web oficial.
{sys, [
{lib_dirs, ["../apps", "../deps"]},
{erts, [{mod_cond, derived}, {app_file, strip}]},
{app_file, strip},
{rel, "webserver", "1.0", [
kernel,
stdlib,
sasl,
inets,
mimetypes,
webserver
]},
{rel, "start_clean", "", [
kernel,
stdlib
]},
{boot_rel, "webserver"},
{profile, embedded},
{incl_cond, derived},
{mod_cond, derived},
{excl_archive_filters, [".*"]}, %% Do not archive built libs
{excl_sys_filters, [
"^bin/.*",
"^erts.*/bin/(dialyzer|typer)",
"^erts.*/(doc|info|include|lib|man|src)"]
},
{excl_app_filters, ["\.gitignore"]},
{app, webserver, [{mod_cond, app}, {incl_cond, include}]}
]}.
144
Ecosistema Erlang
{target_dir, "webserver"}.
{overlay, [
{mkdir, "log/sasl"},
{copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
{copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
{copy, "files/webserver", "bin/webserver"},
{copy, "files/webserver.cmd", "bin/webserver.cmd"},
{copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
{copy, "files/install_upgrade.escript",
"bin/install_upgrade.escript"},
{copy, "files/sys.config",
"releases/\{\{rel_vsn\}\}/sys.config"},
{copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}
]}.
$ rebar generate
==> rel (generate)
console
En primer plano. Abre una consola y ejecuta todas las aplicaciones
mientras vemos en pantalla los mensajes que imprime cada una de
las aplicaciones al lanzarse.
start / stop
Estos comandos permiten iniciar y detener la aplicacin que se
lanza en segundo plano.
ping
Hace un ping al nodo de la aplicacin. En caso de que est activo el
nodo responder con un pong.
attach
Permite conectarse a una aplicacin ejecutndose en segundo
plano.
$ webserver/bin/webserver start
145
Ecosistema Erlang
(webserver@127.0.0.1)1>
Importante
Cuando nos conectamos a una aplicacin en ejecucin con attach
debemos siempre salir con la pulsacin de las teclas Control+D.
Si salimos interrumpiendo la consola la aplicacin se detendr.
Nota
Una buena prctica es que cada vez que demos una versin como
terminada, hagamos una compilacin en un fichero comprimido
de la misma. Esto nos servir para poder transportar nuestro
proyecto a produccin y crear actualizaciones.
6. Actualizando en Caliente
Una de las ventajas que reseamos de Erlang al principio es su capacidad
para cambiar el cdigo en caliente sin necesidad de detener la ejecucin
del programa. En la Seccin 8, Recarga de cdigo del Captulo 5,
Procesos vimos cmo cargar cdigo en caliente. En esta seccin veremos
cmo realiza esta accin rebar para cambiar el cdigo en caliente de todo
un proyecto completo.
-module(fileserver).
-export([send/1]).
146
Ecosistema Erlang
">>).
send(Request) ->
case proplists:get_value(path, Request, "/") of
"/help" ->
Content = ?HELP_TEXT,
Size = list_to_binary(
integer_to_list(byte_size(Content))
),
<<
?RESP_200/binary, "text/html",
"\nContent-lenght: ", Size/binary,
"\n\n", Content/binary
>>;
"/" ++ Path ->
{ok, CWD} = file:get_cwd(),
RealPath = filename:join(CWD, Path),
case file:read_file(RealPath) of
{ok, Content} ->
Size = list_to_binary(
integer_to_list(byte_size(Content))
),
Type = mimetype(Path),
<<
?RESP_200/binary, Type/binary,
"\nContent-lenght: ", Size/binary,
"\n\n", Content/binary
>>;
{error, _} ->
?RESP_404
end
end.
mimetype(File) ->
case filename:extension(string:to_lower(File)) of
".png" -> <<"image/png">>;
".jpg" -> <<"image/jpeg">>;
".jpeg" -> <<"image/jpeg">>;
".zip" -> <<"application/zip">>;
".xml" -> <<"application/xml">>;
".css" -> <<"text/css">>;
".html" -> <<"text/html">>;
".htm" -> <<"text/html">>;
".js" -> <<"application/javascript">>;
".ico" -> <<"image/vnd.microsoft.icon">>;
_ -> <<"text/plain">>
end.
147
Ecosistema Erlang
{"2.0", [
{"1.0", [
{load_module,fileserver}
]}
], [
{"1.0", [
{load_module,fileserver}
]}
]}.
148
Ecosistema Erlang
Nota
El fichero appup permite muchos ms comandos. Si los
cambios han sido ms significativos como la agregacin o
eliminacin de mdulos se pueden emplear otros comandos
como add_module y/o delete_module. El sistema tambin permite
trazar la dependencia de mdulos y el orden en el que se deben
de ir cargando a travs de las opciones PrePurge, PostPurge y
DepMods de las formas completas de las tuplas de comandos que
12
pueden verse en la web oficial de appup . Por ejemplo:
{add_module, filesystem},
{add_module, ftp},
{load_module, webserver},
{code_change, [{webserver, undefined}]},
{delete_module, fileserver}
$ cp webserver_2.0.tar.gz webserver_old/releases
$ webserver_old/bin/webserver upgrade webserver_2.0
Unpacked Release "2.0"
Installed Release "2.0"
Made Release "2.0" Permanent
12
http://www.erlang.org/doc/man/appup.html
13
http://www.erlang.org/doc/man/sys.html#Mod:system_code_change-4
149
Ecosistema Erlang
Importante
Para que el sistema no falle habr que revisar la configuracin
del nombre de nodo en el fichero vm.args dentro del directorio
files antes de realizar la generacin del producto final. Es
recomendable emplear sname y solo indicar el nombre del nodo
eliminando el nombre de la mquina.
7. Guiones en Erlang
Los guiones son un tipo de programacin en la que se desarrolla un
cdigo de forma rpida para servir de guin a una tarea automatizada.
Normalmente por un administrador de sistemas. Dentro de las tareas ms
usuales de los guiones se encuentran el lanzamiento, monitorizacin y
parada de aplicaciones.
#!/usr/bin/env escript
main([]) ->
io:format("Usage: fact <number>~n~n"),
1;
main([NumberTxt]) ->
try
Number = list_to_integer(NumberTxt),
io:format("~p! = ~p~n", [Number, fact(Number)]),
0
catch
error:badarg -> main([])
end.
fact(1) -> 1;
fact(N) -> N * fact(N-1).
Nota
La primera lnea es conocida como shebang o hashbang. Indica el
comando con el que hay que ejecutar ese guin.
150
Ecosistema Erlang
$ chmod +x fact
$ ./fact
Usage: fact <number>
$ ./fact a
Usage: fact <number>
$ ./fact 12
12! = 479001600
14
El comando rebar escriptize se encarga tomar todos los mdulos
compilados de un proyecto e introducirlos en un fichero binario y
ejecutable. Esto permite la distribucin fcil de ese script y su instalacin
en el sistema de ficheros.
-module(fact).
-export([main/1]).
main([]) ->
io:format("Usage: fact <number>~n~n"),
1;
main([NumberTxt]) ->
try
Number = list_to_integer(NumberTxt),
io:format("~p! = ~p~n", [Number, fact(Number)]),
0
catch
error:badarg -> main([])
end.
fact(1) -> 1;
fact(N) -> N * fact(N-1).
{application, fact, [
{description, "Factorial"},
{vsn, "1.0"},
{applications,[
kernel, stdlib, inets
]}
]}.
14
Aunque hay versiones de rebar en las que la ayuda no lo muestra existe y funciona en esas mismas
funciones.
151
Ecosistema Erlang
8. El camino a OTP
Como ya avanc en la introduccin este libro consta de dos partes.
En esta primera parte hemos visto todo lo necesario para conocer el
lenguaje y el funcionamiento de la mquina virtual de Erlang. Hemos
repasado cmo trabajar con los proyectos. Hemos formado nuestra
mente a un nuevo conocimiento y a una nueva forma de hacer las cosas.
Sin embargo en los proyectos profesionales de Erlang se emplea y con
mucha frecuencia OTP.
152
Apndices
Apndice A. Instalacin de Erlang
Tener la mquina virtual de Erlang operativa con todas sus caractersticas
es bastante fcil gracias a la gran cantidad de instaladores y
distribuciones preparadas que existen en la web. Las versiones oficiales
1
se ofrecen desde la pgina web oficial de Erlang .
1. Instalacin en Windows
Aunque siempre recomiendo GNU/Linux o incluso algn sistema BSD
para programar y desarrollar software, las preferencias de cada uno son
distintas y hay muchos usuarios y programadores que prefieren Windows
a cualquier otro sistema operativo.
2
La descarga para Windows se puede realizar desde la web de descargas
de la pgina oficial de Erlang. Entre los paquetes que hay para descargar
3
se puede encontrar Windows Binary File . Se trata de un instalador que
nos guiar paso a paso en la instalacin.
1
http://erlang.org/
2
http://www.erlang.org/download.html
3
Tambin se encuentra la versin de 64 bits para los que tengan sistemas Windows de 64 bits.
154
Instalacin de Erlang
Nota
La instalacin de la versin R12B02 requiere de la instalacin de
unas DLLs que son propiedad de Microsoft. El instalador inicia un
proceso de instalacin para estas libreras en las que habr que
aceptar las licencias y acuerdos de uso de las propias libreras.
155
Instalacin de Erlang
hace mucho tiempo las versiones de Erlang disponibles pueden ser algo
antiguas.
https://www.erlang-solutions.com/downloads/download-erlang-otp
Una vez instalado podemos ejecutar desde consola el comando erl o erlc
entre otros.
# wget http://www.erlang.org/download/otp_src_R15B02.tar.gz
# tar xzf otp_src_R15B02.tar.gz
# cd otp_src_R15B02
# ./configure
# make && make install
Importante
Sistemas como Ubuntu no disponen acceso directo como usuario
root. En su lugar se debe de acceder a root a travs del comando
sudo. Para realizar la accin anterior sin que surjan problemas,
deberemos de ejecutar antes: sudo su.
156
Instalacin de Erlang
3. Otros sistemas
La empresa Erlang Solutions provee paquetes de instalacin para otros
sistemas como MacOS X. En este sistema podemos optar por instalar este
4
paquete o por la instalacin desde otros sistemas como MacPorts .
4
http://www.macports.org/
157
Apndice B. La lnea de
comandos
El cdigo de la mayora de ejemplos del libro han sido desarrollados
en la consola o lnea de comandos. Erlang como mquina virtual
dispone de esta lnea de comandos para facilitar su gestin y demostrar
su versatilidad permitiendo conectar una consola a un nodo que se
encuentre en ejecucin y permitir al administrador obtener informacin
del servidor en ejecucin.
Importante
Las funciones que se listan a continuacin estn disponibles solo
en la lnea de comandos, no es posible emplearlos en el cdigo
de un programa convencional.
1. Registros
Los registros se comentaron en la Seccin1.6, Registros del Captulo2,
El lenguaje. En la consola se pueden gestionar los registros a travs de
las siguientes funciones:
rd(R,D)
Define un registro en la lnea de comandos:
rl() / rl(R)
Muestra todos los registros definidos en la lnea de comandos en el
primer caso y solo el registro pasado como parmetro en el segundo
caso. La definicin se muestra como se escribira dentro de un
fichero de cdigo.
rf() / rf(R)
Elimina la definicin de los registros cargados. La primera forma
elimina todos los registros mientras que la segunda solo elimina el
registro pasado como parmetro R.
158
La lnea de comandos
2. Mdulos
Indicaremos todos los comandos referentes a la compilacin, carga e
informacin para los mdulos:
c(FoM)
Compila un fichero pasando su nombre como parmetro. El nombre
proporcionado ser un tomo con el nombre del mdulo o una
cadena que indique el nombre del fichero, opcionalmente con su
ruta.
l(M)
Permite cargar un mdulo. Conviene recordar lo ya mencionado
sobre la carga de mdulos en la Seccin8, Recarga de cdigo del
Captulo5, Procesos.
m() / m(M)
Muestra todos los mdulos cargados en memoria en el primer caso
e informacin detallada del mdulo cargado en el segundo caso.
Se muestra informacin como la fecha y hora de compilacin, la
ruta de dnde se encuentra el mdulo en el sistema de ficheros, las
funciones que exporta y las opciones de compilacin.
lc([F])
Lista de ficheros a compilar.
nl(M)
Carga el mdulo indicado en todos los nodos conectados.
nc(FoM)
Compila y carga el mdulo o fichero en todos los nodos conectados.
y(F)
Genera un analizador Yecc, el fichero pasado como parmetro debe
de ser un fichero con sintaxis vlida para Yecc.
1
Las opciones que se pueden usar con rr/3 son las mismas que se pueden emplear para la compilacin.
159
La lnea de comandos
3. Variables
En la lnea de comandos se pueden emplear variables. Estas variables
tienen el comportamiento de nica asignacin igual que el cdigo que
podemos escribir en cualquier mdulo. Las siguientes funciones nos
permiten gestionar estas variables:
b()
Muestra todas las variables empleadas o enlazadas (binding) a un
valor en la lnea de comandos.
f() / f(X)
Indica a la lnea de comandos que olvide (forget) todas las variables
o solo la indicada como parmetro.
4. Histrico
La consola dispone de un histrico que nos permite repetir comandos
ya utilizados en la consola. El histrico es configurable y contendr
los ltimos comandos tecleados. El smbolo del sistema (o prompt) nos
indicar el nmero de orden que estamos ejecutando.
e(N)
Repite el comando con orden N segn el smbolo de sistema de la
consola.
h()
Muestra el histrico de comandos ejecutados.
history(N)
Configura el nmero de entradas que sern almacenadas como
histrico.
results(N)
Configura el nmero de resultados que sern almacenados como
histrico.
160
La lnea de comandos
v(N)
Obtiene el resultado de la lnea correspondiente pasada como
parmetro. A diferencia de e(N) el comando no se vuelve a
ejecutar, solo se muestra el resultado del comando N ejecutado
anteriormente.
5. Procesos
Estas son funciones rpidas y de gestin sobre los temas que ya se
revisaron en el Captulo5, Procesos:
bt(Pid)
Obtiene el trazado de pila del proceso en ejecucin.
flush()
Muestra todos los mensajes enviados al proceso de la consola.
i(X,Y,Z)
Muestra informacin de un proceso dando sus nmeros como
argumentos separados de la funcin. La informacin obtenida es el
estado de ejecucin del proceso, procesos a los que est enlazado,
la cola de mensajes, el diccionario del proceso y memoria utilizada
entre otras opciones ms.
pid(X,Y,Z)
Obtiene el tipo de dato PID de los nmeros dados.
regs() / nregs()
Lista todos los procesos registrados (con nombre) en el nodo actual
o en todos los nodos conectados respectivamente.
catch_exception(B)
Cada ejecucin se realiza mediante un evaluador. Cuando se lanza
una excepcin el evaluador es regenerado por el proceso de la
consola. Esto provoca que se pierdan tablas ETS entre otras cosas. Si
ejecutamos esta funcin con true el evaluador captura la excepcin
y no muere.
i() / ni()
Muestra todos los procesos del nodo o de todos los nodos
conectados respectivamente.
161
La lnea de comandos
6. Directorio de trabajo
En cualquier momento podemos modificar el directorio de trabajo dentro
de la consola. Las siguientes funciones nos ayudan en esta y otras tareas
relacionadas:
cd(Dir)
Cambia el directorio de trabajo. Se indica una lista de caracteres con
la ruta relativa o absoluta para el cambio.
ls() / ls(Dir)
Lista el directorio actual u otro indicado como parmetro de forma
relativa o absoluta a travs de una lista de caracteres.
pwd()
Imprime el directorio de trabajo actual.
7. Modo JCL
Cuando se presiona la combinacin de teclas Control+G se accede a
una nueva consola. Esta consola es denominada JCL (Job Control Mode o
modo de control de trabajos). Este modo nos permite lanzar una nueva
consola, conectarnos a una consola remota, detener una consola en
ejecucin o cambiar de una a otra consola.
Importante
Cada trabajo que se lanza es una consola (shell). Este modo nos
permite gestionar estas consolas. Cada nodo puede tener tantas
consolas como se quiera.
c [nn]
Conectar a una consola. Si no se especifica un nmero vuelve al
actual.
i [nn]
Detiene la consola actual o la que corresponda al nmero que se
indique como argumento. Es til cuando se quiere interrumpir un
bucle infinito sin perder las variables empleadas.
k [nn]
Mata la consola actual o la que corresponda al nmero que se
indique como argumento.
162
La lnea de comandos
j
Lista las consolas en ejecucin. La consola actual se indicar con un
asterisco (*).
s [shell]
Inicia una consola nueva. Si se indica el nombre de un mdulo como
argumento se intentar lanzar un proceso con ese mdulo como
consola alternativa.
r [node [shell]]
Indica que deseamos crear una consola en un nodo al que se
tiene conexin. Se lanza una consola en ese nodo y queda visible
en el listado de consolas. Se puede indicar tambin una consola
alternativa en caso de disponer de ella.
q
Finaliza la ejecucin del nodo Erlang en el que estemos ejecutando
el modo JCL.
8. Salir de la consola
Para salir de la consola hay varias formas. Se puede salir presionando
dos veces la combinacin de teclas Control+C, ejecutando la funcin de
consola q() o a travs del modo JCL y su comando q.
163
Apndice C. Herramientas
grficas
Erlang es una mquina virtual adems de un lenguaje por lo que requiere
de herramientas que le permitan gestionar sus procesos de una forma
fcil para el usuario. En el captulo Captulo5, Procesos tratamos la forma
en la que listar los procesos de consola. Ahora veremos la forma de ver
estos procesos de forma grfica, as como las tablas ETS y Mnesia y la
depuracin de los procesos que ejecutemos.
1. Barra de herramientas
Para facilitar la tarea de acceder al conjunto de herramientas grficas
disponemos de toolbar. Esta aplicacin nos proporciona una ventana con
botones de acceso directo a las herramientas de la interfaz grfica de
Erlang.
> toolbar:start().
tv
Table Visualizer o visor de tablas. Se emplea para poder visualizar
el contenido de las tablas ETS y las que maneja la base de datos
Mnesia.
pman
Process Manager o administrador de procesos. Es la versin grfica
de lo que conseguimos con las funciones de consola i/0, i/1 y
otras funciones como exit/1 integradas en una nica interfaz.
debugger
El depurador nos permite seguir la ejecucin de un cdigo en la
ventana de proceso y revisar los datos de sus variables en ese
momento.
164
Herramientas grficas
appmon
Application Monitor o monitor de aplicaciones. Permite ver la carga
que supone en el nodo la aplicacin y el rbol de dependencia de
procesos entre otras opciones.
Nota
En los mens podemos ver opciones como Add GS contributions
que agrega otros cuatro botones extra: el juego bonk, mandelbrot
que es un generador de fractales, el juego othello y el juego Cols.
otp_src_R15B02/lib/gs/contribs
2. Monitor de aplicaciones
El monitor de aplicaciones nos proporciona informacin sobre las
aplicaciones ejecutadas en la mquina virtual de Erlang. El concepto de
aplicacin proviene de OTP y se coment en el Captulo 8, Ecosistema
Erlang.
En el grfico adjunto se puede ver el nombre del nodo (con fondo negro)
del que cuelgan todas las aplicaciones que se han sido lanzadas.
165
Herramientas grficas
Info
Se abrir una nueva ventana que mostrar la informacin del
proceso.
Send
Permite enviar un mensaje al proceso. Es equivalente a:
Pid ! Mensaje
Trace
Cada mensaje recibido al proceso marcado es impreso por la
consola. Es equivalente a la funcin erlang:trace/3.
Kill
Enva la seal de finalizacin al proceso. Equivalente a exit/1.
166
Herramientas grficas
Nota
El monitor de aplicaciones no solo puede visualizar el estado de
las aplicaciones del nodo en el que fue lanzado, sino que tambin
puede ver el estado de las aplicaciones de otros nodos a los que
se encuentre conectado.
3. Gestor de procesos
Una forma simple de gestionar los procesos que se ejecutan en la
mquina virtual de Erlang es a travs de su gestor de procesos grfico.
El gestor de procesos nos permite visualizar de forma rpida y en
tiempo real los procesos que se estn ejecutando en la mquina virtual
de Erlang su PID, nombre registrado (si tienen), la funcin actual que
estn ejecutando, los mensajes que tienen pendientes en el buon, las
reducciones aplicadas y el tamao que ocupa el proceso.
Nota
El gestor de procesos no solo puede visualizar y gestionar los
procesos del nodo en el que fue lanzado, sino que tambin puede
ver y gestionar los procesos de otros nodos a los que se encuentre
conectado.
167
Herramientas grficas
4. Visor de tablas
Las tablas ETS y las que se crean con la base de datos de Mnesia estn
disponibles para su visualizacin a travs de esta interfaz. Por defecto
nos muestra un listado de las tablas ETS que hay creadas en el nodo.
La informacin que se muestra en la tabla principal es: nombre de la
tabla, identificador de la tabla, PID del propietario de la tabla, nombre
registrado del proceso propietario (si tiene) y tamao de la tabla (en
nmero de entradas).
Nota
El visualizador de tablas no solo puede visualizar y gestionar las
tablas ETS y Mnesia del nodo en el que fue lanzado, sino que
tambin puede hacer lo mismo con otros nodos a los que se
encuentre conectado.
168
Herramientas grficas
5. Observer
El programa observer es una unificacin de pman, tv y appmon adems
de otras caractersticas ms en un entorno wxWindow mejorado con
respecto a los anteriores.
> observer:start().
System
Ofrece informacin del sistema: versin y arquitectura de la
mquina virtual de Erlang, CPUs e hilos en ejecucin, uso de la
memoria y estadsticas de uso.
Load Charts
Se presentan tres grficos en tiempo real: utilizacin del
programador de tareas (equivalente a la carga del procesador), uso
de la memoria y uso de la Entrada/Salida.
Applications
Ofrece la misma informacin que appmon. Visualiza el listado de
aplicaciones a la izquierda y el rbol de procesos a la derecha, previa
seleccin de una de las aplicaciones.
Processes
Listado de procesos tal y como se presentaban tambin en pman.
Se muestra una tabla con los procesos activos y su informacin:
PID, nombre o funcin inicial, reducciones, memoria, mensajes
encolados y funcin en ejecucin actualmente.
Table Viewer
Lista las tablas ETS y Mnesia como lo hace la aplicacin tv. A
diferencia de tv, observer no permite la creacin de nuevas tablas
ETS aunque s permite modificar y/o eliminar entradas de las tablas
existentes.
Trace Overview
Tanto la aplicacin appmon como pman permiten trazar procesos.
La aplicacin observer unifica esto en una sola pestaa cambiando
la forma en la que realizar las trazas de los procesos.
169
Herramientas grficas
Nota
Observer no solo puede actuar en el nodo en el que es lanzado,
sino que tambin puede interactuar con otros nodos a los que se
encuentre conectado.
6. Depurador
Esta es quizs la herramienta ms importante y que mejor se debera
de aprender a utilizar para poder analizar el cdigo realizado de una
forma ms cercana al dato. Aunque las trazas sern suficientes en algunos
casos, siempre es mejor depurar un cdigo que nos origina un error que
no conseguimos entender bien que abarcar el problema al modo prueba-
ensayo-error.
> debugger:start(local).
170
Herramientas grficas
171
Herramientas grficas
Nota
El depurador se puede lanzar en dos modos: local o global. Por
defecto se lanza de modo global, por lo que cualquier mdulo que
se interprete ser mostrado en la ventana del monitor.
http://www.erlang.org/doc/apps/debugger/debugger_chapter.html
172
173