Está en la página 1de 189

Erlang/OTP

Volumen I: Un Mundo Concurrente


Manuel Angel Rubio Jimnez
Erlang/OTP
Volumen I: Un Mundo Concurrente
Manuel Angel Rubio Jimnez

Resumen

El lenguaje de programacin Erlang naci sobre el ao 1986 en los laboratorios


Ericsson de la mano de Joe Armstrong. Es un lenguaje funcional con base
en Prolog, tolerante a fallos, y orientado al trabajo en tiempo real y a la
concurrencia, lo que le proporciona ciertas ventajas en lo que a la declaracin
de algoritmos se refiere.

Como la mayora de lenguajes funcionales Erlang requiere un anlisis del


problema y una forma de disear la solucin diferente a como se hara en un
lenguaje de programacin imperativo. Sugiere una mejor y ms eficiente forma
de llevarlo a cabo. Se basa en una sintaxis ms matemtica que programtica
por lo que tiende ms a la resolucin de problemas que a la ordenacin y
ejecucin de rdenes.

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.

En este libro se recoge un compendio de informacin sobre lo que es el


lenguaje, cmo cubre las necesidades para las que fue creado, cmo sacarle
el mximo provecho a su forma de realizar las tareas y a su orientacin a la
concurrencia. Es un repaso desde el principio sobre cmo programar de una
forma funcional y concurrente en un entorno distribuido y tolerante a fallos.

Erlang/OTP, Volumen I: Un Mundo Concurrente por Manuel ngel Rubio


1
Jimnez se encuentra bajo una Licencia Creative Commons Reconocimiento-
2
NoComercial-CompartirIgual 3.0 Unported .

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

3.2. Lanzar una excepcin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43


3.3. La estructura try...catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.4. Errores de ejecucin ms comunes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4. Las funciones y mdulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
1. Organizacin del cdigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2. mbito de las funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3. Polimorfismo y Concordancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4. Guardas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5. Clausuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6. Programacin Funcional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7. Recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
7.1. Ordenacin por mezcla (mergesort) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
7.2. Ordenacin rpida (quicksort) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8. Funciones Integradas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
5. Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
1. Anatoma de un Proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
2. Ventajas e inconvenientes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
3. Lanzando Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4. Bautizando Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5. Comunicacin entre Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
6. Procesos Enlazados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
7. Monitorizacin de Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
8. Recarga de cdigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
9. Gestin de Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
10. Nodos Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
11. Procesos Remotos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
12. Procesos Locales o Globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
13. RPC: Llamada Remota a Proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
14. Diccionario del Proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6. ETS, DETS y Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1. ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.1. Tipos de Tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1.2. Acceso a las ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1.3. Creacin de una ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.4. Lectura y Escritura en ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.5. Match: bsquedas avanzadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
1.6. Eliminando tuplas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
1.7. ETS a fichero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
2. DETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
2.1. Tipos de Tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
2.2. Crear o abrir una DETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
2.3. Manipulacin de las DETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
2.4. De ETS a DETS y viceversa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
3. Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
3.1. Abriendo y Cerrando Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
3.2. Lectura de Ficheros de Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
3.3. Escritura de Ficheros de Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

iv
Erlang/OTP

3.4. Lectura de Ficheros Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103


3.5. Escritura de Ficheros Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
3.6. Acceso aleatorio de Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
3.7. Lecturas y Escrituras por Lotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
4. Gestin de Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.1. Nombre del fichero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.2. Copiar, Mover y Eliminar Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
4.3. Permisos, Propietarios y Grupos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
5. Gestin de Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
5.1. Directorio de Trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.2. Creacin y Eliminacin de Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
5.3. Es un fichero? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
5.4. Contenido de los Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
7. Comunicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
1. Conceptos bsicos de Redes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
1.1. Direcciones IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
1.2. Puertos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
2. Servidor y Cliente UDP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
3. Servidor y Cliente TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
4. Servidor TCP Concurrente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
5. Ventajas de inet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
8. Ecosistema Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
1. Iniciar un Proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
1.1. Instalar rebar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
1.2. Escribiendo el Cdigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
2. Compilar y Limpiar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
3. Creando y lanzando una aplicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
4. Dependencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
5. Liberar y Desplegar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
6. Actualizando en Caliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
7. Guiones en Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
8. El camino a OTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Apndices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
A. Instalacin de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
1. Instalacin en Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
2. Instalacin en sistemas GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
2.1. Desde Paquetes Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
2.2. Compilando el Cdigo Fuente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
3. Otros sistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
B. La lnea de comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
1. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
2. Mdulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
3. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
4. Histrico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
5. Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6. Directorio de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
7. Modo JCL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

v
Erlang/OTP

8. Salir de la consola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163


C. Herramientas grficas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
1. Barra de herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
2. Monitor de aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
3. Gestor de procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
4. Visor de tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
5. Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
6. Depurador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

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.

Tras intercambiar algunos emails, enseguida me di cuenta de la similitud


de nuestras ideas y objetivos: escribir programas eficientes y escalables.
Y aunque no lo conoca personalmente, simplemente con ver su dilatada
experiencia en un abanico tan amplio de tecnologas, ya intu que el
material que saldra de su cabeza sera de ayuda para todo tipo lectores.

En un mundo donde predomina la programacin imperativa, los


lenguajes funcionales vuelven a cobrar importancia por su potencia
y sencillez. La necesidad de sistemas que sean capaces de gestionar
millones de usuarios concurrentes de manera eficiente, ha provocado
que Erlang sea relevante dos dcadas despus de su creacin.

Mi primera experiencia con Erlang fue como ver Matrix, teniendo en


cuenta que todos los conocimientos que tena estaban basados en
lenguajes orientados a objetos, el primer instinto fue extrapolarlos a
aquel primer reto al que me enfrentaba (iterar sobre una lista). Con el
paso de los das empec a comprender que el salto que estaba realizando
no era como aprender otro lenguaje ms (saltar entre PHP, Java o Ruby),
estaba aprendiendo una nueva forma de pensar y resolver problemas la
esencia de los lenguajes funcionales.

Cabe destacar, que los conceptos y herramientas que proporciona


de manera nativa Erlang, te permiten disear y desarrollar desde el
inicio sistemas robustos, evitando tener que resolver problemas de
escalabilidad y operaciones complejas en las siguientes fases de un
proyecto (capas de cache complejas, despliegues en produccin sin
interrupciones, optimizacin de la mquina virtual, ...).

La introduccin al lenguaje propuesta por Manuel Angel, desde la base,


pasando por los tipos de datos y expresiones, y terminando con las
funcionalidades nativas que lo diferencian, ayudarn tanto a lectores
noveles, como a lectores con experiencia en programacin funcional.
Cuando alguien me comenta que quiere aprender Erlang suelo decir tres
cosas:

1. Intenta con todas tus fuerzas olvidar todo lo que sepas de


programacin imperativa;

2. Lee un buen libro, completo, desde la introduccin hasta las reseas;

vii
Prlogo

3. Ten siempre una consola a mano para ir poniendo en prctica los


conocimientos adquiridos.

Hasta el momento de la publicacin de este libro, ese consejo estaba


muy condicionado al conocimiento de ingls de la persona que lo reciba;
y si sumamos todos los nuevos conceptos al que el lector se enfrenta, el
resultado no siempre era el esperado. Gracias a este libro, con un estilo
claro y directo, ejemplos tiles y el reflejo de la experiencia del autor,
har que aprender este lenguaje sea una experiencia productiva, de la
que espero nazcan desde simples algoritmos rpidos y eficientes, hasta
sistemas distribuidos altamente escalables.

Jos Luis Gordo Romero

viii
Introduccin
Sorprendernos por algo es el primer paso de la
mente hacia el descubrimiento.
Louis Pasteur

1. Acerca del autor


La programacin es un tema que me ha fascinado desde siempre. A partir
del ao 2002, a la edad de 22 aos, me centr en perfeccionar mis
conocimientos sobre C++, el paradigma de la orientacin a objetos y sus
particularidades de implementacin en este lenguaje.

El lenguaje C++ me abri las puertas de la orientacin a objetos y ese


mismo ao ya comenc a interesarme por Java. Al ao siguiente, en 2003,
aprend SQL, Perl y PHP, comenzando as una aventura que me ha llevado
al aprendizaje de nuevos lenguajes de programacin regularmente,
siempre con el inters de analizar sus potencias y debilidades. As es
como experiment tambin con lenguajes clsicos como Basic, Pascal,
Modula-2 y otro tipo de lenguajes de scripting para la gestin de sistemas
informticos como Perl o lenguajes de shell.

En los siguientes 8 aos, despus de haber tratado con lenguajes


imperativos, tanto estructurados como orientados a objetos, con sus
particularidades y ecosistemas como son C/C++, Java, Perl, Python, PHP,
Ruby, Pascal y Modula-2 entre otros, descubr Erlang.

En Erlang encontr un mundo en el que es posible desarrollar estructuras


complejas cliente-servidor, en el que los procesos son concurrentes,
distribuidos, robustos y tolerantes a fallos. Por si fuera poco, estas
estructuras se crean mediante un cdigo compacto y con una sintaxis
clara, elegante y fcilmente comprensible.

En el ao 2006, encabec algunos desarrollos en una oficina de I+D


en Crdoba que no resultaron del todo favorables. En aquella poca, el
desarrollo mediante lenguajes imperativos, y las estructuras propias de
concurrencia y distribucin, hicieron que la creacin de soluciones fuese
excesivamente costosa y se terminase desechando.

En 2008 volv a retomar el desarrollo de sistemas del rea de voz,


principalmente en telefona. Con el bagaje de la experiencia anterior
y dispuestos a aplicar las mejores soluciones que proporciona Erlang,
encabec una serie de proyectos para entornos de telecomunicaciones.
Estos proyectos los desarrollamos con xito con una escasa cantidad
de cdigo, en unos tiempos y con una calidad y robustez que parecera

ix
Introduccin

imposible en otros lenguajes. Igualmente comprobamos la simpleza,


efectividad y la capacidad de escalado que brinda el lenguaje y su
mquina virtual.

Con todo lo aprendido y hecho en torno a este lenguaje, me he dado


cuenta de que hace falta llenar el hueco que deja el no tener literatura
sobre Erlang en nuestro idioma, y de paso tratar los temas desde otro
punto de vista.

Puedo lanzarme a esta tarea no sin antes recomendar la literatura


existente que me ha servido como referencia durante mi propio proceso
de aprendizaje y paso a enumerar. El libro de Joe Armstrong sobre Erlang,
completsimo y centrado en el lenguaje base que he reledo decenas de
veces. El de Francesco Cesarini, igualmente recomendable, aunque ms
orientado al desarrollo de proyectos en Erlang. Incluso otro gran libro
1
que he conocido recientemente del equipo que mantiene Erlware , muy
orientado al framework OTP y la metodologa que propone.

2. Acerca de los Revisores


Este libro ha sido revisado por dos personas, sin las cuales, de seguro
que sera mucho ms complejo de seguir. En esta seccin hacemos una
pequea presentacin de ambos.

Jos Luis Gordo Romero


Apasionado de la tecnologa y del software libre. Durante mi
carrera profesional he recorrido distintas reas tecnolgicas, lo
que me ha permitido afrontar proyectos teniendo una perspectiva
global. Empec por la administracin y automatizacin de sistemas,
pasando por el desarrollo hasta llegar al diseo de arquitectura (en
entornos web).

Trabajar en startups me ha permitido explorar y profundizar en


diferentes tecnologas, adems de poder colaborar en varios
proyectos de software libre (de los cuales disfruto aprendiendo y
aportando todo lo que puedo).

Actualmente estoy centrado en varios proyectos donde Erlang es


la base, as que haber podido ayudar en la revisin y escribir el
prlogo, ha sido todo un placer.

Juan Sebastin Prez Herrero


Soy experto en diversas tecnologas, con abultada experiencia en
entornos web, plataformas de movilidad, integracin y gestin
1
http://erlware.com/

x
Introduccin

de proyectos internacionales y entornos open source de lo ms


diversos.

He sido compaero de trabajo de Manuel, lo que ha servido


para un enriquecimiento mutuo, tanto en conocimientos como en
estrategia. Su amplio espectro de conocimiento aporta muchos
patrones y antipatrones, su carcter templado hace que siempre
se pueda llegar a acuerdos y sus indicaciones, especialmente en
el procesamiento a tiempo real y de sistemas con alto nmero de
transacciones me han sido de gran inters.

Me gusta participar en proyectos estimulantes y la edicin de


este libro junto con el aprendizaje de Erlang, era una oportunidad
de divertirme haciendo una tarea nueva como es la edicin de
literatura tcnica en espaol que no estaba dispuesto a dejar pasar.

He realizado la edicin de varios captulos y ha resultado ser una


experiencia ms que interesante. Ponerse en la piel de un lector
y facilitarle las cosas sin bajar demasiado el nivel tcnico es un
reto. Espero que el resultado, probablemente mejorable, facilite la
lectura y comprensin de la obra y por ende su difusin.

El presente libro permite formarse en programacin concurrente


en Erlang de una forma entretenida. Ya estoy deseando leer la
segunda parte sobre OTP, ya que intuyo que para proyectos de
cierta envergadura se requieren unas directrices claras y el uso
de buenos patrones de diseo, sobre todo si se busca la robustez
que normalmente requieren proyectos crticos que en muchas
ocasiones manejan transacciones monetarias.

3. Acerca del libro


Durante el ao 2008, estuvimos trabajando en proyectos de fuerte
concurrencia para el tratamiento de llamadas telefnicas. Buscbamos
que los tiempos de respuesta, la robustez y la agilidad en los desarrollos
fuera la necesaria para este tipo de sistemas. Tras haber empleado
diversas herramientas y tcnicas de replicacin y comparticin en base
de datos sin obtener resultados totalmente satisfactorios nos decidimos
a introducir Erlang. Con ello pudimos observar las capacidades de
este lenguaje y lo bien que se adaptaba a nuestras necesidades de
rendimiento. Fue entonces cuando nos dimos cuenta de que haba muy
poca cantidad de informacin acerca del lenguaje (aunque poco a poco
se vaya subsanando), y mucho menos en castellano.

Es complejo adentrarse en un lenguaje nuevo que nada tiene que


ver con lenguajes con los que se haya trabajado anteriormente (salvo
excepciones como Lisp, Scheme o Prolog), por lo que me decid a escribir

xi
Introduccin

el libro que me hubiese gustado encontrar. Un libro con las palabras


justas y los diagramas apropiados para poder entender ms rpidamente
todos los conceptos nuevos que se ponen delante del programador de
Erlang y OTP.

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.

4. Objetivo del libro


Con este libro pretendo cubrir principalmente los aspectos ms
importantes dentro del mbito de aprendizaje de un nuevo lenguaje de
programacin:

Explicar los aspectos bsicos del lenguaje para comenzar a programar.


Ya que Erlang no es un lenguaje imperativo, puede ocurrir que su
sintaxis sea paradjicamente ms fcil para el que no sabe programar
que para desarrolladores avanzados de lenguajes como C, Java o PHP.

Conocer las fortalezas y debilidades del lenguaje. Como en el uso de


cualquier tecnologa es importante tener la capacidad de seleccionar
un lenguaje o entorno frente a otro dependiendo del trabajo que se
vaya a realizar. En este texto analizamos qu es Erlang y en qu se
puede emplear, con lo que se obtendr una idea clara de posibles
casos de uso cuando tenga que acometer un nuevo desarrollo.

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.

Hay desarrollos que en una versin temprana se han abandonado


completamente y se han recomenzado de otra forma. Ya sea con otros
lenguajes, herramientas, libreras o frameworks. El hecho de toparse con
impedimentos tan grandes de salvar ha provocado que una reescritura
desde cero sea con frecuencia lo ms simple y rpido.

Para ampliar el conocimiento y la posibilidad de eleccin, sobre


todo ahora que se incrementa el nmero de sistemas concurrentes,
de alta disponibilidad, tolerantes a fallos y que deben prestar un
servicio continuo en la red de redes, el presente libro proporciona el
conocimiento de lo que es Erlang, lo que es OTP, y lo que significan estas

xii
Introduccin

nuevas herramientas que va ganando cada vez ms relevancia en los


entornos mencionados.

5. A quin va dirigido este libro?


Este libro est dirigido a todo aqul que quiera aprender a programar
en un lenguaje funcional con control de concurrencia y distribuido.
Permite igualmente ampliar el vocabulario de programacin del lector
con nuevas ideas sobre el desarrollo de programas y la resolucin de
problemas. Esto se aplica tanto a los que comienzan a programar como a
los que ya saben programar, y a aquellos que quieren saber qu puede
hacer este lenguaje para tomarlo en consideracin en las decisiones
tecnolgicas de su empresa o proyecto.

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.

Para el programador experimentado ofrece un nexo hacia un lenguaje


diferente, si el lector proviene del mundo imperativo, o bien
relativamente similar a otros vistos (si se tienen conocimientos de
Lisp, Scheme, Prolog o Haskell). Provee un acercamiento detallado a las
entraas de un sistema desarrollado con una ideologa concreta y para
un fin concreto. Incluso si ya se conoce Erlang supone un recorrido por lo
que ya se sabe, pero desde otro enfoque y con caractersticas o detalles
que probablemente no se conozcan.

Para el desarrollador, analista o arquitecto, ofrece el punto de vista


de una herramienta, un lenguaje y un entorno, en el que se pueden
desarrollar un cierto abanico de soluciones de forma rpida y segura.
Erlang es un lenguaje con muchos aos de desarrollo, probado en
produccin por muchas empresas conocidas y desconocidas. Permite
realizar un recorrido por las potencias del lenguaje y obtener el
conocimiento de sus debilidades. Lo suficiente como para saber si es una
buena herramienta para desarrollar una solucin especfica.

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.

La coleccin, por tanto, consta de dos volmenes. Cada volumen tiene


como misin explorar Erlang de una forma diferente, desde un punto de
vista diferente, y con un objetivo diferente. Los volmenes son:

xiii
Introduccin

Un mundo concurrente. En esta parte nos centraremos en conocer la


sintaxis del lenguaje, sus elementos ms comunes, sus estructuras, los
tipos de datos, el uso de los ficheros y comunicaciones a travs de la
red. Ser el bloque ms extenso, ya que detalla toda la estructura del
lenguaje en s.

Las bases de OTP. Nos adentramos en el conocimiento del sistema OTP,


el framework actualmente ms potente para Erlang y que viene con su
instalacin base. Se vern los generadores de servidores, las mquinas
de estados, los supervisores y manejadores de eventos, entre otros
elementos.

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", []).

Adems de ejemplos con cdigo Erlang, en los distintos apartados del


libro hay diferentes bloques que contienen notas informativas o avisos
importantes. Sus formatos son los siguientes:

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.

Respecto al libro, he de agradecer al equipo con el que estuve


trabajando en Jet Multimedia: Guillermo Rodrguez, Mara Luisa de
la Serna, Jonathan Mrquez, Margarita Ortiz y Daniel Lpez; el que
cada desarrollo que nos planteasen pudisemos verlo como un
desafo a nosotros mismos y sacar lo mejor de nosotros mismos,
as como aprender de cada situacin, de cada lenguaje y de cada
herramienta. Aprend mucho con ellos y espero que podamos seguir
aprendiendo all donde nos toque estar y, si volvemos a coincidir,
muchsimo mejor.

Tambin agradecer a Jos Luis Gordo por su revisin, el prlogo


escrito y sus buenos consejos as como su crtica constructiva, ha
sido un aliado inestimable en esta aventura y un baln de oxgeno
en momentos arduos.

A Juan Sebastin Prez, por brindarse tambin a aprender el


lenguaje de manos de este manuscrito, as como corregir tambin
mi forma de expresarme en algunos puntos que confieso fueron
complicados.

Por ltimo pero no por ello menos importante, agradecer a mi


hermano Rafael y a Luz (Bethany Neumann) el diseo de la portada
y contraportada del libro, as como el logotipo de BosqueViejo.

Jos Luis Gordo


Agradecer a Manuel Angel su confianza por haberme dejado aportar
mi pequeo granito de arena a este proyecto. Adems de ampliar
conocimientos, me ha dado la oportunidad de conocer mejor su
trabajo y a l personalmente, descubriendo su increble energa y
motivacin, sin la cual este libro nunca hubiera visto la luz.

Juan Sebastin Prez


Gracias a Manuel por compartir tantos cafs (descafeinados) e ideas.
A otros compaeros en lo profesional y personal, especialmente al
departamento de movilidad de Jet Multimedia por compartir fatigas
y xitos. Y como no, a mi familia, amigos y pareja que me han

xv
Introduccin

apoyado en el desarrollo de mis habilidades en otros aspectos de


la vida.

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

Erlang comienza a ser un entorno y un lenguaje de moda. La existencia


creciente de empresas orientadas a la prestacin de servicios por
internet con un elevado volumen de transacciones (como videojuegos
en red o sistemas de mensajera mvil y chat) hace que en sitios como
los Estados Unidos, Reino Unido o Suecia proliferen las ofertas de trabajo
que solicitan profesionales en este lenguaje. Existe una necesidad
imperiosa de desarrollar entornos con las caractersticas de la mquina
de Erlang, y la metodologa de desarrollo proporcionada por OTP.

En este captulo introducimos el concepto de Erlang y OTP. Su significado,


caractersticas e historia. La informacin de este primer captulo se
completa con las fuentes que lo han motivado y se provee informacin
precisa sobre dnde se ha extrado cada seccin.

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:

Erlang como lenguaje


Hay muchas discusiones concernientes a si Erlang es o no un
lenguaje funcional. En principio, est entendido que s lo es, aunque
tenga elementos que le hagan salirse de la definicin pura. Por
ello Erlang podra mejor catalogarse como un lenguaje hbrido, al
tener elementos de tipo funcional, de tipo imperativo, e incluso
algunos rasgos que permiten cierta orientacin a objetos, aunque
no completa.

Donde encaja mejor Erlang, al menos desde mi punto de vista, es


como un lenguaje orientado a la concurrencia. Erlang tiene una gran
facilidad para la programacin distribuida, paralela o concurrente y
adems con mecanismos para la tolerancia a fallos. Fue diseado
desde un inicio para ejecutarse de forma ininterrumpida. Esto
significa que se puede cambiar el cdigo de sus aplicaciones sin
detener su ejecucin. Ms adelante explicaremos cmo funciona
esto concretamente.

1
Lo que debes
saber sobre Erlang

Erlang como entorno de ejecucin


Como hemos mencionado antes Erlang es una plataforma de
desarrollo que proporciona no slo un compilador, sino tambin
una mquina virtual para su ejecucin. A diferencia de otros
lenguajes interpretados como Python, Perl, PHP o Ruby, Erlang se
pseudocompila y su mquina virtual le proporciona una importante
capa de abstraccin que le dota de la capacidad de manejar y
distribuir procesos entre nodos de forma totalmente transparente
(sin el uso de libreras especficas).

La mquina virtual sobre la que se ejecuta el cdigo pseudo-


compilado de Erlang, que le proporciona todas las caractersticas de
distribucin y comunicacin de procesos, es tambin una mquina
1
que interpreta un pseudocdigo mquina que nada tiene que ver,
a ese nivel, con el lenguaje Erlang. Esto ha permitido la proliferacin
de los lenguajes que emplean la mquina virtual pero no el lenguaje
en s, como pueden ser: Reia, Elixir, Efene, Joxa o LFE.

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.

Recibe el nombre de Agnus Kraup Erlang. A veces se piensa que el


nombre es una abreviacin de ERicsson LANGuage, debido a su uso
intensivo en Ericsson. Segn Bjarne Dcker, jefe del Computer Science
Lab en su da, esta dualidad es intencionada.

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.

Cambiar el cdigo en caliente


Tambin es importante en el entorno de Ericsson, y en la mayora
de sistemas crticos o sistemas en produccin de cualquier ndole,
que el sistema no se detenga nunca, aunque haya que realizar
actualizaciones. Por ello se agreg tambin como caracterstica el
hecho de que el cdigo pudiese cambiar en caliente, sin necesidad
de parar el sistema y sin que afectase al cdigo en ejecucin.

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

Las estructuras de datos se simplifican tambin bastante y su


potencia, al igual que en lenguajes como Prolog o Lisp, se basa en
las listas.

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.

Paso de mensajes en lugar de memoria compartida


Uno de los problemas de la programacin concurrente es la
ejecucin de secciones crticas de cdigo para acceso a porciones
de memoria compartida. Este control de acceso acaba siendo un
cuello de botella ineludible. Para simplificar e intentar eliminar
el mximo posible de errores, Erlang/OTP se basa en el paso
de mensajes en lugar de emplear tcnicas como semforos
o monitores. El paso de mensajes hace que un proceso sea
el responsable de los datos y la seccin crtica se encuentre
slo en este proceso, de modo que cualquiera que pida
ejecutar algo de esa seccin crtica, tenga que solicitrselo
al proceso en cuestin. Esto abstrae al mximo la tarea de
desarrollar programas concurrentes, simplificando enormemente
los esquemas y eliminando la necesidad del bloque explcito.

Hace no mucho encontr una presentacin bastante interesante sobre


2
Erlang , en la que se agregaba, no slo todo lo que comentaba Armstrong
que deba de tener su sistema para poder desarrollar las soluciones
de forma ptima, sino tambin la contraposicin, el porqu no lo pudo
encontrar en otros lenguajes.

En principio hay que entender que propsito general se refiere al uso


generalizado de un lenguaje a lo ms cotidiano que se suele desarrollar.
Como es obvio, es ms frecuente hacer un software para administracin
de una empresa que un sistema operativo. Los lenguajes de propsito
general sern ptimos para el desarrollo general de ese software de
gestin empresarial, seguramente no tanto para ese software del sistema
operativo. PHP por ejemplo, es un fabuloso lenguaje de marcas que
facilita bastante la tarea a los desarrolladores web y sobre todo a
maquetadores que se meten en el terreno de la programacin. Pero es
algo completamente desastroso para el desarrollo de aplicaciones de
scripting para administradores de sistemas.

En s, los lenguajes ms difundidos hoy en da, como C# o Java, presentan


el problema de carecer de elementos a bajo nivel integrados en sus
2
http://www.it.uu.se/edu/course/homepage/projektDV/ht05/uppsala.pdf

4
Lo que debes
saber sobre Erlang

sistemas que les permitan desarrollar aplicaciones concurrentes de


forma fcil. Esta es la razn de que en el mundo Java comience a hacerse
cada vez ms visible un lenguaje como Scala.

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.

La idea de Erlang surgi por la necesidad de Ericsson de acotar un


problema que haba surgido en su plataforma AXE, que estaba siendo
desarrollada en PLEX, un lenguaje propietario. Joe Armstrong junto a dos
colegas, Elshiewy y Robert Virding, desarrollaron una lgica concurrente
de programacin para canales de comunicacin. Esta lgebra de telefona
permita a travs de su notacin describir el sistema pblico de telefona
(POTS) en tan slo quince reglas.

A travs del inters de llevar esta teora a la prctica desarrollaron


modelos en Ada, CLU, Smalltalk y Prolog entre otros. As descubrieron
que el lgebra telefnica se procesaba de forma muy rpida en sistemas
de alto nivel, es decir, en Prolog, con lo que comenzaron a desarrollar un
sistema determinista en l.

La conclusin a la que lleg el equipo fue que, si se puede resolver


un problema a travs de una serie de ecuaciones matemticas y
portar ese mismo esquema a un programa de forma que el esquema
funcional se respete y entienda tal y como se formul fuera del
entorno computacional, puede ser fcil de tratar por la gente que
entiende el esquema, incluso mejorarlo y adaptarlo. Las pruebas
realmente se realizan a nivel terico sobre el propio esquema, ya
que algortmicamente es ms fcil de probarlo con las reglas propias
de las matemticas que computacionalmente con la cantidad de
combinaciones que pueda tener.

Prolog no era un lenguaje pensado para concurrencia, por lo que


se decidieron a realizar uno que satisfaciera todos sus requisitos,
basndose en las ventajas que haban visto de Prolog para conformar
su base. Erlang vi la luz en 1986, despus de que Joe Armstrong se
encerrase a desarrollar la idea base como intrprete sobre Prolog, con
un nmero reducido de instrucciones que rpidamente fue creciendo
gracias a su buena acogida. Bsicamente, los requisitos que se buscaban
cumplir eran:
3
http://www.erlang-factory.com/upload/presentations/247/erlang_vm_1.pdf

5
Lo que debes
saber sobre Erlang

Los procesos deban de ser una parte intrnseca del lenguaje, no una
librera o framework de desarrollo.

Deba poder ejecutar desde miles a millones de procesos concurrentes


y cada proceso ser independiente del resto, de modo que si alguno de
ellos se corrompiese no daase el espacio de memoria de otro proceso.
Este requisito nos lleva a que el fallo de los procesos debe de ser
aislado del resto del programa.

Debe poder ejecutarse de modo ininterrumpido, lo que obliga a que


para actualizar el cdigo del sistema no se deba detener su ejecucin,
sino que se recargue en caliente.

En 1989, el sistema estaba comenzando a dar sus frutos, pero surgi


el problema de que su rendimiento no era el adecuado. Se lleg a la
conclusin de que el lenguaje era adecuado para la programacin que
se realizaba, pero tendra que ser, al menos unas 40 veces ms rpido.

Mike Williams se encarg de escribir el emulador, cargador, planificador y


recolector de basura (en lenguaje C) mientras que Joe Armstrong escriba
el compilador, las estructuras de datos, el heap de memoria y la pila; por
su parte Robert Virding se encargaba de escribir las libreras. El sistema
desarrollado se optimiz a un nivel en el que consiguieron aumentar su
rendimiento en 120 veces de lo que lo haca el intrprete en Prolog.

En los aos 90, tras haber conseguido desarrollar productos de la


gama AXE con este lenguaje, se le potenci agregando elementos como
distribucin, estructura OTP, HiPE, sintaxis de bit o compilacin de
patrones para matching. Erlang comenzaba a ser una gran pieza de
software, pero tena varios problemas para que pudiera ser adoptado de
forma amplia por la comunidad de programadores. Desafortunadamente
para el desarrollo de Erlang, aqul periodo fue tambin la dcada de Java
y Ericsson decidi centrarse en lenguajes usados globalmente por lo que
prohibi seguir desarrollando en Erlang.

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.

Con el tiempo, la imposicin de no escribir cdigo en Erlang se fue


olvidando y la comunidad de programadores de Erlang comenz a crecer
fuera de Ericsson. El equipo OTP se mantuvo desarrollando y soportando

6
Lo que debes
saber sobre Erlang

Erlang que, a su vez, continu como sufragador del proyecto HiPE y


aplicaciones como EDoc o Dialyzer.

Antes de 2010 Erlang agreg capacidad para SMP y ms recientemente


para multi-core. La revisin de 2010 del emulador de BEAM se ejecuta
con un rendimiento 300 veces superior al de la versin del emulador en
C, por lo que es 36.000 veces ms rpido que el original interpretado en
Prolog. Cada vez ms sectores se hacen eco de las capacidades de Erlang
y cada vez ms empresas han comenzado desarrollos en esta plataforma
por lo que se augura que el uso de este lenguaje siga al alza.

4. Desarrollos con Erlang


Los desarrollos en Erlang cada vez son ms visibles para todos sobre todo
en el entorno en el que Erlang se mueve: la concurrencia y la gestin
masiva de eventos o elementos sin saturarse ni caer. Esto es un punto
esencial y decisivo para empresas que tienen su nicho de negocio en
Internet y que han pasado de vender productos a proveer servicios a
travs de la red.

En esta seccin veremos la influencia de Erlang y cmo se va asentando


en el entorno empresarial y en las comunidades de software libre y el
tipo de implementaciones que se realizan en uno y otro mbito.

4.1. Sector empresarial


Debido a las ventajas intrnsecas del lenguaje y su entorno se ha hecho
patente la creacin de modelos reales MVC para desarrollo web. Merecen
mencin elementos tan necesarios como ChicagoBoss o Nitrogen, cuyo
uso pueden verse en empresas como la espaola Tractis.

Tambin es conocido el caso de Facebook que emplea Erlang en su


implementacin de chat para soportar los mensajes de sus 70 millones
de usuarios. Al igual que Tuenti que tambin emplea esta tecnologa.
4
La empresa inglesa Demonware , especializada en el desarrollo
y mantenimiento de infraestructura y aplicaciones servidoras para
videojuegos en Internet, comenz a emplear Erlang para poder soportar
el nmero de jugadores de ttulos tan afamados como Call of Duty.

Varias empresas del sector del entretenimiento que fabrican


aplicaciones mviles tambin se han sumado a desarrollar sus
aplicaciones de parte servidora en Erlang/OTP. Un ejemplo de este tipo
5
de empresas es Wooga .
4
http://www.erlang-factory.com/conference/London2011/speakers/MalcolmDowse
5
http://es.slideshare.net/wooga/erlang-the-big-switch-in-social-games

7
Lo que debes
saber sobre Erlang

WhatsApp, la aplicacin actualmente ms relevante para el intercambio


y envo de mensajes entre smartphones emplea a nivel de servidor
sistemas desarrollados en Erlang.

Una de las empresas estandarte de Erlang ha sido Kreditor, que cambi


6
su nombre a Klarna AB . Esta empresa se dedica al pago por Internet y
pas en 7 aos a tener 600 empleados.

En el terreno del desarrollo web comienzan a abrirse paso tambin


7
empresas espaolas como Mikoagenda . Es un claro ejemplo de
desarrollo de aplicaciones web ntegramente desarrolladas con Erlang a
nivel de servidor.

Desde que surgi el modelo Cloud, cada vez ms empresas de software


estn prestando servicios online en lugar de vender productos, por lo
que se enfrentan a un uso masificado por parte de sus usuarios, e incluso
a ataques de denegacin de servicio. Estos escenarios junto con servicios
bastante pesados e infraestructuras no muy potentes hacen cada vez ms
necesarias herramientas como Erlang.
8
En la web de Aprendiendo Erlang mantienen un listado mixto de
software libre y empresas que emplean Erlang.

4.2. Software libre


Hay muchas muestras de proyectos de gran envergadura de muy diversa
ndole creados en base a Erlang. La mayora de ellos se centra en
entornos en los que se saca gran ventaja de la gestin de concurrencia y
distribucin que realiza el sistema de 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.

El siguiente listado se muestra como ejemplo:

Base de Datos Distribuidas

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 .

CMS (Content Management System)


20 21
Zotonic , sistema CMS que permite el diseo de pginas web de
forma sencilla a travs de la programacin de las vistas (DTL) y la
gestin del contenido multimedia, texto y otros aspectos a travs del
interfaz de administracin.

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

para comprobar cmo rinden en produccin real o cmo podran rendir


en entornos de pruebas controlados.

Comenzar por comentar el caso de la empresa Demonware, de la que


ya coment algo en la seccin de uso de Erlang en el Sector empresarial,
pero esta vez lo detallar con datos que aport la propia compaa a
travs de Malcolm Dowse en la Erlang Factory de Londrs de 2011.

Despus veremos el banco de pruebas que realiz Joe Armstrong sobre


un servicio empleando un par de configuraciones de Apache y Yaws.

5.1. El caso de Demonware


En la conferencia de Erlang Factory de Londrs, en 2011, Malcolm Dowse,
de la empresa Demoware (de Dubln), dict una ponencia titulada Erlang
and First-Person Shooters (Erlang y los Juegos en Primera Persona).
Decenas de millones de fans de Call of Duty Black Ops testearon la carga
de Erlang.

Demonware es la empresa que trabaja con Activision y Blizzard dando


soporte de los servidores de juegos multi-jugador XBox y PlayStation.
La empresa se constituy en 2003 y desde esa poca hasta 2007 se
mantuvieron modificando su tecnologa para optimizar sus servidores,
hasta llegar a Erlang.

En 2005 construyeron su infraestructura en C++ y MySQL.


Su concurrencia de usuarios no superaba los 80 jugadores,
afortunadamente no se vieron en la situacin de superar esa cifra.
Adems, el cdigo se colgaba con frecuencia, lo que supona un grave
problema.

En 2006 se reescribi toda la lgica de negocio en Python. Se segua


manteniendo a nivel interno C++ con lo que el cdigo se haba hecho
difcil de mantener.

Finalmente, en 2007, se reescribi el cdigo de los servidores de C++ con


Erlang. Fueron unos 4 meses de desarrollo con el que consiguieron que
el sistema ya no se colgase, que se mejorase y facilitase la configuracin
del sistema (en la versin C++ era necesario reiniciar para reconfigurar,
lo que implicaba desconectar a todos los jugadores). Tambin se dot
de mejores herramientas de log y administracin y se haca ms fcil
desarrollar nuevas caractersticas en muchas menos lneas de cdigo.
Para entonces haban llegado a los 20 mil usuarios concurrentes.

A finales de 2007 lleg Call of Duty 4, que supuso un crecimiento


constante de usuarios durante 5 meses continuados. Se pas de 20 mil
a 2,5 millones de usuarios. De 500 a 50 mil peticiones por segundo. La
empresa tuvo que ampliar su nodo de 50 a 1850 servidores en varios

11
Lo que debes
saber sobre Erlang

centros de datos. En palabras de Malcolm: fue una crisis para la compaa,


tenamos que crecer, sin el cambio a Erlang la crisis podra haber sido un
desastre.

Demonware es una de las empresas que ha visto las ventajas de Erlang.


La forma en la que implementa la programacin concurrente y la gran
capacidad de escalabilidad. Gracias a estos factores, han podido estar a la
altura de prestar el servicio de los juegos en lnea ms usados y jugados
de los ltimos tiempos.

5.2. Yaws contra Apache


24
Es bastante conocido ya el famoso grfico sobre la comparativa que
realizaron Joe Armstrong y Ali Ghodsi entre Apache y Yaws. La prueba es
bastante fcil, de un lado, un servidor, de otro, un cliente para medicin
y 14 clientes para generar carga.

La prueba propuesta era generar un ataque de denegacin de servicio


(DoS), que hiciera que los servidores web, al recibir un nmero de
peticiones excesivo, fuesen degradando su servicio hasta dejar de darlo.
Es bien conocido que este hecho pasa con todos los sistemas, ya que los
recursos de un servidor son finitos. No obstante, por su programacin,
pueden pasar cosas como las que se visualizan en el grfico:

En gris oscuro (marcando el punto con un crculo y ocupando las lneas


superiores del grfico) puede verse la respuesta de Yaws en escala de
24
http://www.sics.se/~joe/apachevsyaws.html

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).

En este caso, pasa algo parecido a lo visto con Demonware en la seccin


anterior, Apache no puede procesar ms de 4000 peticiones simultneas,
en parte debido a su integracin ntimamente ligada al sistema operativo,
que le limita. Sin embargo, Yaws se mantiene con el mismo rendimiento
hasta llegar a superar las 80 mil peticiones simultneas.

Erlang est construido con gestin de procesos propia y desligada del


sistema operativo. En s, suele ser ms lenta que la que proporciona el
sistema operativo, pero sin duda la escalabilidad y el rendimiento que se
consigue pueden paliar ese hecho. Cada nodo de Erlang puede manejar
en total unos 2 millones de procesos.

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.

La mayora de personas comienzan programando en lenguajes como


Basic, Modula-2 o Pascal, que tienen una sintaxis muy parecida entre
ellos. Lo mismo pasa con la rama de C/C++, Java y Perl o PHP, que tienen
una sintaxis, el uso de los bloques condicionales, iterativos y declaracin
de funciones y clases tambin semejantes.

En los lenguajes imperativos la sintaxis se basa en la consecucin de


mandatos que el programador enva a travs del cdigo a la mquina. En
Erlang y dems lenguajes funcionales, la sintaxis est diseada como si
se tratara de la definicin de una funcin matemtica o una proposicin
lgica. Cada elemento dentro de la funcin tiene un propsito: obtener
un valor; el conjunto de todos esos valores, con o sin procesamiento,
conforma el resultado. Un ejemplo bsico:

area(Base, Altura) -> Base * Altura.

En este ejemplo puede verse la definicin de la funcin area. Los


parmetros requeridos para obtener su resultado son Base y Altura. A
la declaracin de parmetros le sigue el smbolo de consecucin (->),
como si se tratase de una proposicin lgica. Por ltimo est la operacin
interna que retorna el resultado que se quiere obtener.

Al tratarse de funciones matemticas o proposiciones lgicas no existe


una correlacin entre imperativo y funcional. Para un cdigo imperativo
comn como el que sigue:

para i <- 1 hasta 10 hacer


si clavar(i) = 'si' entonces
martillea_clavo(i)
fsi
fpara

No existe en Erlang un equivalente que pueda transcribir una accin


imperativa como tal. Para desarrollar en Erlang hay que pensar en el qu
se quiere hacer ms que en el cmo. Si en un lenguaje funcional lo que se
quiere es clavar los clavos que seleccione la funcin clavar martilleando,
se podra hacer a travs de una lista de comprensin:

14
El lenguaje

[ martillea_clavo(X) || X <- Clavos, clavar(i) =:= 'si' ].

Hay que entender que para resolver problemas de forma funcional


muchas veces la mentalidad imperativa es un obstculo. Tenemos que
pensar en los datos que tenemos y qu datos queremos obtener como
resultado. Es lo que nos conducir a la solucin.

Erlang es un lenguaje de formato libre. Se pueden insertar tantos


espacios y saltos de lnea entre smbolos como se quiera. Esta funcin
area es completamente equivalente a la anterior a nivel de ejecucin:

area(
Base,
Altura
) ->
Base * Altura
.

A lo largo de este captulo revisaremos la base del lenguaje Erlang.


Veremos lo necesario para poder escribir programas bsicos de propsito
general y entender esta breve introduccin de una forma ms detallada
y clara.

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.

Un tomo es una palabra que comienza por una letra en minscula y va


seguido de letras en mayscula o minscula, nmeros y/o subrayados.
Tambin se pueden emplear letras en mayscula al inicio, espacios y lo

15
El lenguaje

que queramos, siempre y cuando encerremos la expresin entre comillas


simples. Algunos ejemplos:

> 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

Los tomos tienen como nica finalidad ayudar al programador a


identificar estructuras, algoritmos y cdigo especfico.

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.

1.2. Nmeros Enteros y Reales


En Erlang, los nmeros pueden ser de dos tipos, tal y como se ve en este
ejemplo de cdigo en la consola:

> is_float(5).
false
> is_float(5.0).
true
> is_integer(5.0).
false
> is_integer(5).

16
El lenguaje

true

Otra de las cosas que sorprende de Erlang es su precisin numrica.


Si multiplicamos nmeros muy altos veremos como el resultado sigue
mostrndose en notacin real, sin usar la notacin cientfica que
muestran otros lenguajes cuando una operacin supera el lmite de
clculo de los nmeros enteros (o valores errneos por overflow):

> 102410241024 * 102410241024 * 1234567890.


12947972063153419287126752624640

Esta caracterstica hace de Erlang una plataforma muy precisa y adecuada


para clculos de intereses bancarios, tarificacin telefnica, ndices
burstiles, valores estadsticos, posicin de puntos tridimensionales, etc.

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.

El formato de las variables se inicia con una letra mayscula, seguida de


tantas letras, nmeros y subrayados como se necesiten o deseen. Una
variable puede tener esta forma:

> Pi = 3.1415.
3.1415
> Telefono = "666555444".
"666555444"
> Depuracion = true.
true

Sobre las variables se pueden efectuar expresiones aritmticas, en


caso de que contenga nmeros, operaciones de listas o emplearse
como parmetro en llamadas a funciones. Un ejemplo de variables
conteniendo nmeros:

> Base = 2.
2

17
El lenguaje

> Altura = 5.2.


5.2
> Base * Altura.
10.4

Si en un momento dado, queremos que Base tenga el valor 3 en lugar del


valor 2 inicialmente asignado veramos lo siguiente:

> Base = 2.
2
> Base = 3.
** exception error: no match of right hand side value 3

Lo que est ocurriendo es que Base ya est enlazado al valor 2 y que


la concordancia (o match) con el valor 2 es correcto, mientras que si lo
intentamos encajar con el valor 3 resulta en una excepcin.

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

Para eliminar todas las variables que tenga memorizadas la


consola se puede emplear: f().

La ventaja de la asignacin nica es la facilidad de analizar cdigo


aunque muchas veces no se considere as. Si una variable durante toda
la ejecucin de una funcin slo puede contener un determinado valor
1
el comportamiento de dicha funcin es muy fcilmente verificable .

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.

Las listas son una de las potencias de Erlang y otros lenguajes


funcionales. Al igual que en Lisp, Erlang maneja las listas como lenguaje
de alto nivel, en modo declarativo, permitiendo cosas como las listas
de comprensin o la agregacin y eliminacin de elementos especficos
como si de conjuntos se tratase.
1
Muestra de ello es dialyzer, una buena herramienta para comprobar el cdigo escrito en Erlang.

18
El lenguaje

1.4.1. Qu podemos hacer con una lista?


Una lista de elementos se puede definir de forma directa tal y como se
presenta a continuacin:

> [ 1, 2, 3, 4, 5 ].
[1,2,3,4,5]
> [ 1, "Hola", 5.0, hola ].
[1,"Hola",5.0,hola]

A estas listas se les pueden agregar o sustraer elementos con los


operadores especiales ++ y --. Tal y como se presenta en los siguientes
ejemplos:

> [1,2,3] ++ [4].


[1,2,3,4].
> [1,2,3] -- [2].
[1,3]

Otro de los usos comunes de las listas es la forma en la que se puede ir


tomando elementos de la cabecera de la lista dejando el resto en otra
sublista. Esto se realiza con esta sencilla sintaxis:

> [H|T] = [1,2,3,4].


[1,2,3,4]
> H.
1
> T.
[2,3,4]
> [H1,H2|T2] = [1,2,3,4].
[1,2,3,4]
> H1.
1
> H2.
2
> T2.
[3,4]

De esta forma tan sencilla la implementacin de los conocidos


algoritmos de push y pop de insercin y extraccin en pilas resultan tan
triviales como:

> Lista = [].


[]
> Lista2 = [1|Lista].
[1]
> Lista3 = [2|Lista2].
[2,1]
> [Extrae|Lista2] = Lista3.
[2,1]
> Extrae.
2
> Lista2.

19
El lenguaje

[1]

No obstante, el no poder mantener una nica variable para la pila


dificulta su uso. Este asunto lo analizaremos ms adelante con el
tratamiento de los procesos y las funciones.

1.4.2. Cadenas de Texto


Las cadenas de texto son un tipo especfico de lista. Se trata de una
lista homognea de elementos representables como caracteres. Erlang
detecta que si una lista en su totalidad cumple con esta premisa, es una
cadena de caracteres.

Por tanto, la representacin de la palabra Hola en forma de lista, se puede


hacer como lista de enteros que representan a cada una de las letras o
como el texto encerrado entre comillas dobles ("). Una demostracin:

> "Hola" = [72,111,108,97].


"Hola"

Como puede apreciarse, la asignacin no da ningn error ya que ambos


valores, a izquierda y derecha, son el mismo para Erlang.

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.

Al igual que con el resto de listas, las cadenas de caracteres soportan


tambin la agregacin de elementos, de modo que la concatenacin se
podra realizar de la siguiente forma:

> "Hola, " ++ "mundo!".


"Hola, mundo!"

Una de las ventajas de la asignacin propia de que dispone Erlang es


que si encuentra una variable que no ha sido enlazada a ningn valor,
automticamente cobra el valor necesario para que la ecuacin sea
cierta. Erlang intenta hacer siempre que los elementos a ambos lados del
signo de asignacin sean iguales. Un ejemplo:

20
El lenguaje

> "Hola, " ++ A = "Hola, mundo!".


"Hola, mundo!"
> A.
"mundo!"

Esta notacin tiene sus limitaciones, en concreto la variable no asignada


debe estar al final de la expresin, ya que de otra forma el cdigo para
realizar el encaje sera mucho ms complejo.

1.4.3. Listas binarias


Las cadenas de caracteres se forman por conjuntos de enteros, es
decir, se consume el doble de memoria para una cadena de caracteres
almacenada en una lista en Erlang que en cualquier otro lenguaje. Las
listas binarias permiten almacenar cadenas de caracteres con tamao de
byte y permite realizar trabajos especficos con secuencias de bytes o
incluso a nivel de bit.

La sintaxis de este tipo de listas es como sigue:

> <<"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.

Por ejemplo, la forma en la que tombamos la cabeza de la lista en una


variable y el resto lo dejbamos en otra variable, se puede simular de la
siguiente forma:

> <<H:1/binary,T/binary>> = <<"Hola">>.


<<"Hola">>
> H.
<<"H">>
> T.
<<"ola">>

La concatenacin en el caso de las listas binarias no se realiza como


con las listas normales empleando el operador ++. En este caso debe
realizarse de la siguiente forma:

> A = <<"Hola ">>.


<<"Hola ">>
> B = <<"mundo!">>.
<<"mundo!">>
> C = <<A/binary, B/binary>>.

21
El lenguaje

<<"Hola mundo!">>

Para obtener el tamao de la lista binaria empleamos la funcin


byte_size/1. En el caso anterior para cada una de las variables
empleadas:

> byte_size(A).
5
> byte_size(B).
6
> byte_size(C).
11

Esta sintaxis es un poco ms elaborada que la de las listas, pero se debe


a que nos adentramos en la verdadera potencia que tienen las listas
binarias: el manejo de bits.

1.4.4. Trabajando con Bits


En la seccin anterior vimos la sintaxis bsica para simular el
comportamiento de la cadena al tomar la cabeza de una pila. Esta sintaxis
se basa en el siguiente formato: Var:Tamao/Tipo; siendo opcionales
Tamao y Tipo.

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.

En caso de que el tamao no se indique, se asume que es tanto como


el tipo soporte y/o hasta encajar el valor al que debe de igualarse (si es
posible), por ello en el ejemplo anterior la variable T se queda con el
resto de la lista binaria.

Los tipos tambin tienen una forma compleja de formarse, ya que se


pueden indicar varios elementos para completar la definicin de los
mismos. Estos elementos son, en orden de especificacin: Endian-Signo-
Tipo-Unidad; vamos a ver los posibles valores para cada uno de ellos:

Endian: es la forma en la que los bits son ledos en la mquina, si es en


formato Intel o Motorola, es decir, little o big respectivamente. Adems
de estos dos, es posible elegir native, que emplear el formato nativo
de la mquina en la que se est ejecutando el cdigo. El valor por
defecto se prefija big.

> <<1215261793:32/big>>.
<<"Hola">>
> <<1215261793:32/little>>.

22
El lenguaje

<<"aloH">>
> <<1215261793:32/native>>.
<<"Hola">>

En este ejemplo se ve que la mquina de la prueba es de tipo big u


ordenacin Intel.

Signo: se indica si el nmero indicado se almacenar en formato con


signo o sin l, es decir, signed o unsigned, respectivamente.

Tipo: es el tipo con el que se almacena el dato en memoria. Segn el


tipo el tamao es relevante para indicar precisin o nmero de bits,
por ejemplo. Los tipos disponibles son: integer, float y binary.

Unidad: este es el valor de la unidad, por el que multiplicar el tamao.


En caso de enteros y coma flotante el valor por defecto es 1, y en
caso de binario es 8. Por lo tanto: Tamao x Unidad = Nmero de bits;
por ejemplo, si la unidad es 8 y el tamao es 2, los bits que ocupa el
elemento son 16 bits.

Si quisiramos almacenar tres datos de color rojo, verde y azul en 16


bits, tomando para cada uno de ellos 5, 5 y 6 bits respectivamente,
tendramos que la particin de los bits se podra hacer de forma algo
dificultosa. Con este manejo de bits, componer la cadena de 16 bits (2
bytes) correspondiente, por ejemplo, a los valores 20, 0 y 6, sera as:

> <<20:5, 0:5, 60:6>>.


<<"<">>

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:

> bit_size(<<"Hola mundo!").


88

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.

Las tuplas, a diferencia de las listas, no pueden incrementar ni


decrementar su tamao salvo por la redefinicin completa de su
estructura. Se emplean para agrupar datos con un propsito especfico.

23
El lenguaje

Por ejemplo, imagina que tenemos un directorio con unos cuantos


ficheros. Queremos almacenar esta informacin para poder tratarla y
sabemos que va a ser: ruta, nombre, tamao y fecha de creacin.

Esta informacin se podra almacenar en forma de tupla de la siguiente


forma:

{ "/home/yo", "texto.txt", 120, {{2011, 11, 20}, {0, 0, 0}} }.

Las llaves indican el inicio y fin de la definicin de la tupla, y los


elementos separados por comas conforman su contenido.

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:

> {date(), time()}.


{{2011,12,6},{22,5,17}}

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.

1.5.1. Listas de Propiedades


Una lista de propiedades es una lista de tuplas clave, valor. Se gestiona
mediante la librera proplists. Las listas de propiedades son muy usadas
para almacenar configuraciones o en general cualquier informacin
variable que se requiera almacenar.

Supongamos que tenemos la siguiente muestra de datos:

> A = [{path, "/"}, {debug, true}, {days, 7}].

Ahora supongamos que de esta lista, que se ha cargado desde algn


fichero o mediante cualquier otro mtodo, queremos consultar si
debemos de realizar o no la depuracin del sistema, es decir, mostrar
mensajes de log si la propiedad debug es igual a true:

> proplists:get_value(debug, A).

24
El lenguaje

true

Como es muy posible que no se sepan las claves que existen en un


determinado momento dentro de la lista existen las funciones is_defined,
o get_keys para poder obtener una lista de claves de la lista.

Un ejemplo de posible uso como tabla hash sera:

> 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

El empleo de las listas de propiedades de esta forma nos facilita el acceso


a los datos que sabemos que existen dentro de una coleccin (o lista) y
extraer nicamente los que queramos obtener.

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.

Como los registros se emplean a nivel de preprocesador, en la consola


slo podemos definir registros empleando un comando especfico de
consola. Adems, podemos cargar los registros existentes en un fichero
y emplearlos desde la propia consola para definir datos o para emplear
los comandos propios de manejo de datos con registros.

La definicin de registros desde la consola se realiza de la siguiente


forma:

> rd(agenda, {nombre, apellidos, telefono}).

25
El lenguaje

Para declarar un registro desde un archivo el formato es el siguiente:

-record(agenda, {nombre, apellidos, telefono}).

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:

> A = {agenda, "Manuel", "Rubio", 666666666}.


{agenda,"Manuel","Rubio",666666666}
> rr("registros.hrl").
[agenda]
> A.
#agenda{nombre = "Manuel",apellidos = "Rubio",
telefono = 666666666}

Erlang reconoce como primer dato de la tupla el nombre del registro


y como cuenta con el mismo nmero de elementos, si no tenemos en
cuenta el identificador, la considera automticamente como un registro.
Tambin se pueden seguir empleando las funciones y elementos tpicos
de la tupla ya que a todos los efectos sigue sindolo.

Nota
Para obtener la posicin dentro de la tupla de un campo, basta
con escribirlo de la siguiente forma:

#agenda.nombre

Esto nos retornar la posicin relativa definida como nombre con


respecto a la tupla que contiene el registro de tipo agenda.

Para tratar los datos de un registro, podemos realizar cualquiera de las


siguientes acciones:

> 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}

Recordemos siempre que la asignacin sigue siendo nica.

Para acceder al contenido de un dato de un campo del registro,


accederemos indicando que es un registro (dato#registro, A#agenda en
el ejemplo) y despus agregaremos un punto y el nombre del campo al
que queremos acceder.

Para modificar los datos de un registro existente en lugar del punto


emplearemos las llaves. Dentro de las llaves estableceremos tantas
igualdades clave=valor como necesitemos (separadas por comas), tal y
como se ve en el ejemplo anterior.

Para obtener en un momento dado informacin sobre los registros,


podemos emplear la funcin record_info. Esta funcin tiene dos
parmetros, el primero es un tomo que puede contener fields si
queremos que retorne una lista de tomos con el nombre de cada campo;
o size, para retornar el nmero de campos que tiene la tupla donde
se almacena el registro (includo el identificativo, en nuestros ejemplos
agenda).

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:

> A = agenda, record_info(fields, A).

Nos retornar illegal record info.

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

2. Imprimiendo por pantalla


Muchas veces se nos presentar la necesidad de mostrar datos por
pantalla. De momento, toda la informacin que vemos es porque la
consola nos la muestra, como resultado de salida del cdigo que vamos
escribiendo. No obstante, hay momentos, en los que ser necesario
realizar una salida concreta de un dato con informacin ms completa.

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.

Por ejemplo, si quieres mostrar una cadena de texto por pantalla,


podemos escribir lo siguiente:

> io:format("Hola mundo!").


Hola mundo!ok

Esto sale as porque el retorno de la funcin es ok, por lo que se imprime


la cadena de texto y seguidamente el retorno de la funcin (el retorno de
funcin se imprime siempre en consola). Para hacer un retorno de carro,
debemos de insertar un caracter especial. A diferencia de otros lenguajes
donde se usan los caracteres especiales, Erlang no usa la barra invertida,
sino que emplea la virgulilla (~), y tras este smbolo, los caracteres se
interpretan de forma especial. Tenemos:

~
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

> io:format("[~c,~5c,~5.3c,~-5.3c]~n", [$a,$b,$c,$d]).


[a,bbbbb, ccc,ddd ]
ok

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:

> io:format("[~7.2e,~7.2f,~7.4g]", [10.1,10.1,10.1]).


[ 1.0e+1, 10.10, 10.10]ok
> Args = [10000.67, 10123.23, 1220.32],
> io:format("~11.7e | ~11.3f | ~11.7g ", Args).
1.000067e+4 | 10123.230 | 1220.320 ok

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:

> Hola = "Hola mundo!",


> io:format("[~s,~-7s,~-7.5s]", [Hola, Hola, Hola]).
[Hola mundo!,Hola mu,Hola ]ok

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:

> Data = [{hola,mundo},10,"hola",mundo],


> io:format("[~w,~w,~w,~w]~n", Data).
[{hola,mundo},10,[104,111,108,97],mundo]
ok

La versin de W es similar a la anterior aunque toma dos parmetros


de la lista de parmetros. El primero es el dato que se va a imprimir,
el segundo es la profundidad. Si imprimimos una lista con muchos
elementos, podemos mostrar nicamente un nmero determinado
de ellos. A partir de ese nmero agrega puntos suspensivos. Un
ejemplo:

29
El lenguaje

> io:format("[~W]", [[1,2,3,4,5],3]).


[[1,2|...]]ok

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.

Con X se puede emplear un prefijo que se toma del siguiente


parmetro que haya en la lista de parmetros, consecutivo al valor
a representar.

El smobolo de almohadilla (#) siempre antepone la base


en formato Erlang: 10#20 (decimal), 8#65 (octal), 16#1A
(hexadecimal). La diferencia entre las maysculas y minsculas
es precisamente esa, la representacin de las letras de las bases
mayores a 10 en maysculas o minsculas. Un ejemplo:

> io:format("[~.2b,~.16x,~.16#]", [21,21,"0x",21]).


[10101,0x15,16#15]ok

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.

El tiempo tambin se maneja en una tupla de tres elementos en la que se


pueden diferenciar en este orden: hora, minutos y segundos. Un ejemplo
sera el siguiente:

{22,10,5}

Una fecha y hora completa se representa a travs de otra tupla que


contiene en su interior las tuplas mencionadas antes, separadas en dos
elementos diferenciados, es decir, un formato como el siguiente:

> erlang:localtime().
{{2012,5,22},{22,10,5}}

Para obtener la fecha y hora en la zona horaria local podemos emplear


tambin estas otras funciones dentro de una tupla de dos elementos:
{date(), time()}

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:

> {M,S,_} = now(), M*1000000+S.


1337717405

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.

Este mdulo, adems, tiene la capacidad de trabajar con segundos


gregorianos en lugar de POSIX. El nmero obtenido en segundos
3
(para representacin interna) es contado desde el ao cero , en
lugar de 1970. Esto da la posibilidad de dar fechas anteriores a
1970.

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

En este captulo ampliamos lo visto en el captulo anterior con el


conocimiento de las expresiones lgicas, las expresiones aritmticas, las
estructuras de control y el manejo de las excepciones.

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.

Las expresiones pueden ser de tipo aritmtico o lgico. Las aritmticas


buscan un valor a travs de operaciones matemticas simples o
complejas. De un conjunto de datos dados con las operaciones indicadas
y el orden representado por la expresin se obtiene un resultado. En las
lgicas se busca una conclusin lgica (o binaria) a la conjuncin de los
predicados expuestos.

1.1. Expresiones Aritmticas


Con los nmeros, de forma nativa, se pueden llevar a cabo expresiones
aritmticas. Las ms bsicas, como la suma, resta, multiplicacin y
divisin son de sobra conocidas. Otras operaciones como la divisin
entera o el remanente (o mdulo) se implementan en cada lenguaje de
una forma distinta, por lo que haremos un repaso rpido con un breve
ejemplo:

> 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

Se puede hacer uso de los parntesis para establecer una relacin de


precedencia de operadores para, por ejemplo, anteponer una suma a una
multiplicacin. Tambin se pueden realizar operaciones encadenadas,
por ejemplo multiplicando ms de dos operandos. Ejemplos de todo
esto:

> 2 * 3 + 1.
7
> 2 * (3 + 1).
8
> 3 * 3 * 3.
27

1.2. Expresiones Lgicas


Vamos a ver los operadores que se emplean en el lgebra de Boole band
(binary and), bor (binary or) y bxor (binary exclusive or). Estos operadores
tratan los nmeros como binarios y operan con el valor de cada una de
sus posiciones (ceros o unos). Un ejemplo:

> 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.

Tambin podemos encontrarnos con que queremos almacenar el


resultado, o emplear el valor lgico de una serie de comparaciones. Para
ello ya no operamos de forma binaria, sino que obtenemos resultados
binarios nicos como true o false. Podramos hacer:

> 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

Podemos construir todas las expresiones lgicas que queramos de


modo que a nivel de comparacin podamos obtener un resultado

34
Expresiones, Estructuras
y Excepciones

lgico (verdadero o falso). En la siguiente seccin se mencionan todos


los operadores de comparacin que se pueden emplear para realizar
comparaciones entre cadenas, nmeros, tuplas, listas y/o registros.

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.

Esto quiere decir que se evala la primera parte de la expresin


y, en caso de andalso (por ejemplo), si es falsa, ya se sabe que
el resultado general ser falso, por lo que no se comprueba la
segunda parte, retornando inmediatamente el valor false. Son
tiles si la comprobacin se debe hacer consultado una funcin
que tiene un coste de comprobacin asociado, ya que muchas
veces es mejor ahorrarse esas ejecuciones. Lo mismo se aplica a
una comprobacin que pueda fallar por lo que necesitamos otra
anterior que descarta la segunda. Por ejemplo:

is_list(List) andalso length(List)

Si List no fuese una lista, la ejecucin de length/1 fallara. Al


emplear andalso esto no sucede, ya que slo se comprueba la
primera parte, y al obtener false finaliza las comprobaciones.

1.3. Precedencia de Operadores


El orden de los operadores para Erlang de ms prioritario a menos
prioritario es el siguiente:

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.

Estas estructuras se basan en la concordancia de sus expresiones. Ambas


tienen que realizar una concordancia positiva con una expresin y
ejecutar un cdigo que retorne un valor.

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.

Si tenemos un conjunto de datos, por ejemplo una lista, podemos hacer


un simple concordancia haciendo:

[1,2,3] = [1,2,3]

Si realizamos esta asignacin, veremos que nos da como resultado


[1,2,3], es decir, se acepta que el valor de la izquierda es igual al de la
derecha (como en matemticas: es un aserto vlido).

Ahora bien, si tenemos el dato de la derecha que lo desconocemos, como


habamos visto en la listas, podemos hacer:

[A,B,C] = [1,2,3]

36
Expresiones, Estructuras
y Excepciones

Esto nos dar como resultado la asociacin a A, B y C de los valores 1, 2 y


3, respectivamente, por lo que retornar como en el caso anterior, [1,2,3].

En la seccin de listas comentamos ms formas de hacer concordancia a


travs de la agregacin de conjunto (++) o con la lista en formato cabeza-
cola ([H|T]). Con respecto a las tuplas, esto no es aplicable, ya que la tupla
tiene valores fijos, pero podemos ignorar los que no nos interesen de la
siguiente forma:

{A,_,C} = {1,2,3}

Con el smbolo de subrayado (o guin bajo "_"), le decimos al sistema


que en ese espacio debe de haber un dato (del tipo que sea: lista, tupla,
tomo, nmero o registro), pero que no nos interesa.

2.2. Estructura case


La primera estructura de control que vamos a tratar, probablemente la
ms usada, es case. Esta estructura toma un valor inicial como referencia
y busca entre las opciones que se especifican la primera que concuerde
para ejecutar su bloque funcional y retornar el valor que establezca la
eleccin.

Como dijimos en un principio, la denominacin de funcional, implica que


cada accin, estructura y funcin debe retornar un valor. Las estructuras
de control como case no son una excepcin.

Veamos un ejemplo:

> Impuesto = case irpf of


irpf -> 0.25;
iva -> 0.18;
_ -> 0
end.
0.25

En este ejemplo podemos ver cmo, si la estructura que se indica en


case casa con cualquiera que se suceda en las subsiguientes lneas, se
ejecuta un bloque concreto, retornando el resultado de la ejecucin de
dicho bloque (en este ejemplo slo un valor). Si no se encontrase ningn
valor que casara, la estructura no podra retornar nada y dara un error.
Es aconsejable acabar con un subrayado (_) que casa con todo y tomarlo
como valor por defecto, a menos que se quiera expresamente que falle
en caso de que no se contenga un valor apropiado.

Podemos ver otro ejemplo ms complejo como el siguiente:

> Resultado = case Fecha of

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.

Si la variable Fecha la igualamos al retorno de la funcin date() el sistema


entender que casa con el primer bloque, ya que es una tupla de 3
elementos, convertir cada dato y lo concatenar con los guiones para
retornarlo en modo texto con formato A-M-D. Si lo que enviamos es un
texto en una lista binaria separado por barras inclinadas (/), tomar cada
parte y lo representar anlogamente. En caso de no casar con ninguno
de los anteriores, retorna una cadena vaca.

La estructura case puede agregar condicionales a cada opcin para la


1
concordancia. Esto es lo que se conoce como guardas . Estas expresiones
se pueden agregar empleando conexiones como: andalso o "," y orelse o
";". Estas guardas se agregan tras cada opcin con la palabra clave when,
tal y como se ve en el siguiente ejemplo:

> Resultado = case Fecha of


{D,M,A} when is_integer(D),
is_integer(M), is_integer(A) ->
integer_to_list(A) ++ "-" ++
integer_to_list(M) ++ "-" ++
integer_to_list(D);
<<Dia:2/binary,"/",Mes:2/binary,"/",Agno:4/binary>>
when is_binary(Fecha) ->
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.

La diferencia existente entre las tres formas es que el agregado also o


else hace que sea una comprobacin vaga pudiendo finalizar antes de
1
Esta expresin inglesa se ha traducido en sitios como aprendiendo erlang como guardas.

38
Expresiones, Estructuras
y Excepciones

evaluar todos los predicados. Los signos de puntuacin se comportan de


la misma forma en este caso.

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

El uso de or nos da un error de cdigo directamente, ya que estamos


sumando 1 a un tomo llamado a y eso da bad argument in arithmetic
expression. Mediante el uso de orelse no nos da error, pero ignora toda
esa comprobacin por ser errnea, pasando a comprobar el siguiente
bloque y devolviendo fail. Por ltimo, con el signo ";", en lugar de tomar
ese resultado como no vlido e invalidar toda la comprobacin como el
caso anterior, slo da como invlida la primera parte y pasa a comprobar
el siguiente predicado, considerando que la primera parte retorna false.

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.

Si nos fijamos bien esta estructura podra tomarse como una


simplificacin de la estructura case anterior. La nica diferencia radica
en la eliminacin de los bloques de concordancia. Es decir, slo emplea
las guardas.

Por ejemplo, la siguiente estructura if devuelve el caso1 si el da de hoy


est entre los valores 1 y 10, y si es sobre 11 y 20, caso2. En caso de

39
Expresiones, Estructuras
y Excepciones

ejecutarse la funcin mostrada con los valores mayores o iguales a 21


dara un error:

> {A,M,D} = date().


{2012,4,25}
> Caso = if
> (D >= 1) and (D =< 10) -> caso1;
> (D >= 11) and (D =< 20) -> caso2
> end.
** exception error: no true branch found when evaluating an if
expression

Este error es debido a que esta estructura, al igual que el resto de


estructuras existentes en Erlang, debe de retornar un valor y en caso de
no poder ejecutar ningn bloque de cdigo para resolver la funcin o
valor que debe devolver, origina el fallo.

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.

En la misma tabla de precedencia de operadores se puede ver que


and y or tienen ms prioridad que las comparaciones, por lo que,
en caso de que se usen stos y no el punto y coma (;) o la coma
(,) u orelse o andalso, es necesario encerrar la comparacin entre
parntesis.

Para que el sistema no nos falle cuando introduzcamos fechas a partir


del da 21, vamos a definir una accin por defecto:

> Caso = if
> D >= 1 andalso D =< 10 -> caso1;
> D >= 11 andalso D =< 20 -> caso2;
> true -> unknown
> end.

A diferencia de la estructura case, el valor de comodn no se hace sobre


una variable que pueda contener cualquier valor (como en el caso de
subrayado, por ejemplo), sino se emplea la palabra reservada true por
tratarse de predicados lgicos.

2.4. Listas de Comprensin


Una de las ventajas de la programacin funcional es sin duda su caracter
declarativo. El hecho de poder tener una estructura como las listas de
comprensin, nos puede ayudar a extraer informacin sin problemas,

40
Expresiones, Estructuras
y Excepciones

indicando: de donde procede esta informacin, cul queremos que sea su


formato de salida y las condiciones que debe de cumplir nos proporciona
dicha informacin al instante.

Por ejemplo, si queremos sacar de una lista slo los nmeros pares, sera
tan sencillo como:

> [ X || X <- [1,2,3,4,5], X rem 2 =:= 0 ].


[2,4]

Si expresamos esto mismo en lenguaje natural sera algo as como:


[ Dame X || Donde X es un elemento de la lista <- [1,2,3,4,5], tal que la
condicin X rem 2 =:= 0 se cumpla.

Las listas de comprensin tienen tres partes que se enmarcan dentro de


los corchetes. La primera es la proyeccin de los elementos, es decir,
indica la forma en la que se presentarn los datos o en la que queremos
que se configure la salida de la ejecucin de la lista de comprensin.

La segunda es la seleccin de los datos. Esta parte est separada de la


primera por dos pipes (||) y tiene una flecha de derecha a izquierda que
indica a la derecha el origen de los datos y a la izquierda el patrn o forma
de los datos.

La tercera parte, separada por una coma de la anterior, son las


condiciones de la seleccin. Las condiciones que debe de cumplir cada
elemento de la lista, para ser seleccionado. En el caso del ejemplo se
indic que el valor de X deba de ser par (que su remanente fuese cero
en una divisin por dos).

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.

Un truco bastante til que yo empleo es compararlo con una


sentencia SELECT de SQL, ya que tiene la parte de la proyeccin
(inmediatamente despus de SELECT), la parte de la seleccin (la
parte del FROM) y las condiciones de la seleccin para cada tupla
(la parte del WHERE).

Un ejemplo ms completo, teniendo listas de listas, pero siendo una


matriz fija de 2xN, por ejemplo, podemos realizar la siguiente seleccin:

> 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

> [ X || [Y, X] <- A, Y rem 2 =:= 0, X >= 4 ].


[4,6]

La lista resultado nos muestra, dentro de una sublista de dos elementos


a los que asociamos como (Y,X), el hecho de que el elemento Y deba de
ser par y el elemento X mayor o igual a 4. Por lo que, en esta definicin,
concuerdan los nmeros 4 y 6.

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.

El tema de los procesos lo veremos en el siguiente captulo de forma


ms extensa. Ahora vamos a centrarnos en las excepciones, porque,
qu suecede cuando un proceso encuentra un fallo o una situacin
inesperada por el programador? Normalmente se dispara una excepcin
que hace que el proceso muera.

En el siguiente captulo veremos que eso en muchos casos es asumible e


incluso deseable. Pero tambin hay casos en los que, si el cdigo maneja
recursos que hay que tratar de llevar a una situacin segura antes de que
suceda lo inevitable, es preferible intentar de realizar algn tratamiento
para esa excepcin.

3.1. Recoger excepciones: catch


El primer tipo de instruccin que se introdujo en Erlang para la captura
de errores y excepciones es catch. Este comando se puede anteponer
a la ejecucin de una funcin o de cualquier instruccin. Si se genera un
error, catch permite transformarlo en un dato recibido por la funcin
o instruccin que se hubiese ejecutado. Veamos un pequeo ejemplo
desde la consola de Erlang:

> 1 = a.
** exception error: no match of right hand side value a
> catch 1 = a.
{'EXIT',{{badmatch,a},[{erl_eval,expr,3}]}}

La ejecucin de la primera expresin nos lleva a una excepcin


que propocara la finalizacin de ejecucin del proceso, mientras
que anteponiendo catch a la misma expresin, Erlang convierte esa
excepcin en un tipo de dato que se podra procesar a travs de una
estructura de control.

42
Expresiones, Estructuras
y Excepciones

Un ejemplo del uso de catch con case:

> case catch 1 = a of


> true -> caso1;
> false -> caso2;
> {'EXIT',Error} -> casoError
> end.

En este caso, el sistema no produce un error, sino que retorna el


casoError, que debe de ser manejado por el cdigo que toma el retorno
de esta instruccin.

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.

3.2. Lanzar una excepcin


Hay veces que, en lugar de capturar una excepcin conviene provocarla.
Esto se puede hacer de muchas maneras. Podemos emplear asertos
(afirmaciones que se toman como axioma) para que generen una
excepcin en ese punto. Por ejemplo:

> 2+3=5.
5

Si empleamos variables para almacenar los valores, y cometemos un


error:

> A=2, B=3, 5=A+A.


** exception error: no match of right hand side value 4

Como el cdigo es errneo y 5 no es igual a 4, el sistema se detiene


en ese punto. Esto nos garantiza que, si el cdigo es crtico y no debe
de contener errores, en unas pruebas podra aparecer el error y ser
solucionado.

Adems de esta tcnica, podemos lanzar excepciones con mensajes


de error concretos, por si quisiramos a otro nivel capturarlos para
procesarlos. Estos se lanzaran a travs de throw. Podemos verlo ms
claro a travs de un ejemplo:

> throw({fallo, "Esto ha fallado"}).


** exception throw: {fallo,"Esto ha fallado"}

43
Expresiones, Estructuras
y Excepciones

En caso de que quisiramos capturarlo con catch, el sistema trata este


lanzamiento de excepcin como un error real provocado por el usuario,
por lo que se podra capturar como cualquier otro error provocado por
el sistema.

3.3. La estructura try...catch


try...catch es una nueva forma de tratar los errores, ms clara y potente
que catch. Este bloque se presenta como los que existen en los lenguajes
imperativos. La parte try da cabida a ejecucin de cdigo que ser
observado por la estructura y en caso del lanzamiento de cualquier
excepcin, ya sea por fallo, throw o porque se haya ordenado al proceso
acabar su ejecucin, todo esto se puede atrapar en el catch.

Un ejemplo de esta estructura:

> try
> a = 1
> catch
> throw:Term -> Term;
> exit:Razon -> Razon;
> error:Razon -> Razon
> end.
{badmatch,1}

En la parte de catch se declaran tres partes diferenciadas. Estas se


detallan con su clase, que puede ser cualquiera de las tres: throw, exit
o error. A continuacin y despus de los dos puntos (:) est la variable
que contendr el mensaje en s del error para poder emplearlo dentro
del bloque de cdigo de recuperacin.

Esta sentencia presenta tambin una zona en la que poder ejecutar


acciones que se lleven a cabo tanto si el cdigo falla como si no. Esta
seccin recibe el nombre de after, y es un bloque de cdigo que se agrega
tras catch. Por ejemplo, si queremos imprimir por pantalla un saludo falle
o no el cdigo:

> try
> a=1
> catch
> error:Error -> Error
> after
> io:format("Adios~n")
> end.
Adios
{badmatch,1}

El cdigo se ejecuta de modo que, como after est dentro de la estructura,


hasta que esa seccin no termina (en este caso imprimir Adios por
pantalla) la estructura no retorna el valor correspondiente a su ejecucin
(la excepcin a travs de la rama error:Error).

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.

3.4. Errores de ejecucin ms comunes


En esta seccin daremos un repaso a los errores de ejecucin ms
comunes que suelen surgir en Erlang cuando programamos de forma que
el lector pueda corregirlos rpidamente.

function_clause
Cuando se llama a una funcin con parmetros incorrectos, ya sea
en nmero, concordancia o por guardas, se dispara esta excepcin:

> io:format("hola", [], []).


** exception error: no function clause matching...

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.

> case hola of adios -> "" end.


** exception error: no case clause matching hola

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.

> if false -> "" end.


** exception error: no true branch found when eval...

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

> A=1, A=2.


** exception error: no match of right hand side value 2

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:

> A = hola, A(12).


** exception error: bad function hola

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:

> A = fun(_,_) -> ok end, A(uno).

46
Expresiones, Estructuras
y Excepciones

** exception error: interpreted function with arity 2 ...

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).

Si capturamos estos errores, se presentarn de la forma:

{Error, Reason}

Donde Error puede tomar cualquiera de los valores indicados


anteriormente (bararg, function_clause, cause_clause, ...) y Reason tendr
una descripcin de las funciones que fueron llamadas, para llegar a ese
punto.

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

Hasta el momento hemos estado ejecutando el cdigo desde la consola.


Todas las pruebas y cdigos de ejemplo vistos se han escrito pensando
en que sern ejecutados desde la consola de la mquina virtual de Erlang.
Normalmente la programacin en Erlang no se produce en la consola,
sino que se realiza a travs de la escritura de mdulos en los que hay
funciones.

Las funciones se podran tratar como otras estructuras de control (como


case o if), ya que disponen de elementos similares aunque son elementos
de definicin. No se ejecutan en el momento como las estructuras de
control, sino que la ejecucin se realiza mediante una llamada a la
funcin.

En esta seccin revisaremos los conceptos de mdulo, funcin, el


polimorfismo y otros aspectos ms avanzados de funciones y mdulos
que permite Erlang.

1. Organizacin del cdigo


El cdigo en Erlang se organiza en mdulos y dentro de cada mdulo
puedes encontrar funciones. Anteriormente ya hemos visto algunos
de estos mdulos, como el caso de proplists, por ejemplo, en el que
emplebamos el uso de funciones como get_value.

Un mdulo se define en un fichero a travs de unas instrucciones


de preprocesador iniciales que nos permiten definir el nombre y
las funciones que queremos exportar (para emplear desde fuera del
mdulo).

El cdigo podra ser como sigue:

-module(mi_modulo).
-export([mi_funcion/0]).

mi_funcion() ->
"Hola mundo!".

El mdulo del cdigo anterior llamado mi_modulo debe guardarse en un


fichero con el nombre mi_modulo.erl. El mdulo exporta, o pone a

48
Las funciones y mdulos

disposicin de otros mdulos y de la consola la posibilidad de usar la


funcin mi_funcion, cuya aridad (o nmero de parmetros) es cero.

Para simplificar el tema de la exportacin en la codificacin de nuestros


primeros mdulos hasta que nos acostumbremos a ella, podemos obviar
el hecho de que habr funciones privadas para el mdulo y dejarlas todas
abiertas. Esto se hara escribiendo esta cabecera, en lugar de la anterior:

-module(mi_modulo).
-compile([export_all]).

Esta directiva le dice al compilador que exporte todas las funciones de


modo que no haya que nombrarlas una a una en la sentencia export.

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

Esto genera un fichero mi_modulo.beam que ser el que


emplear la mquina virtual para acceder a las funciones creadas.

Tambin es posible compilar un mdulo en la consola de Erlang,


en nuestro ejemplo, escribiendo:

> c(mi_modulo)

Lo cual compilar el cdigo creando el fichero mencionado


anteriormente, dejndolo disponible para su uso.

Desde la consola de la mquina virtual podemos ejecutar:

> mi_modulo:mi_funcion().
"Hola mundo!"

La mquina virtual de Erlang busca el fichero beam en su ruta de mdulos


por defecto y luego en el directorio actual. Si lo encuentra, lo carga y
busca la funcin dentro del mismo. En caso de que no encontrase la
funcin retornara un fallo.

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.

Hay que tener cuidado con el nombre de los mdulos. Por


ejemplo, si se creara un mdulo vaco de nombre erlang y se
intentara cargar el sistema completo se detendra, ya que se
intentaran emplear las funciones del propio sistema Erlang,
esenciales para su funcionamiento, y no estaran presentes en
este nuevo mdulo de fecha ms reciente.

2. mbito de las funciones


Cuando creamos un mdulo podemos importar y exportar funciones
dentro o hacia fuera de l. El mdulo encapsula un conjunto de funciones
que pueden ser accesibles por otros mdulos si se especifica su
exportacin.

La declaracin export es una lista que puede contener tantas referencias


de funciones como se deseen publicar, e incluso pueden existir varias
declaraciones diferentes de export dentro de un mismo mdulo.

La declaracin import, al igual que la anterior, contiene una lista de


funciones como segundo parmetro, que se importan desde el mdulo
que se detalla como primer parmetro. Puede haber tantas declaraciones
como se necesiten dentro de un mdulo y cada declaracin es slo para
la importacin desde un mdulo.

Por ejemplo, tenemos el cdigo de este mdulo:

-module(traductor).
-export([get/1]).
-import(proplists, [get_value/2]).

data() ->
[{"hi", "hola"}, {"bye", "adios"}].

get(Key) ->
get_value(Key, data()).

En este caso y desde el punto de vista de la exportacin, estamos dando


exclusivamente acceso a la funcin get con un parmetro, tanto a otros
mdulos que importasen traductor como a la consola. Desde el punto de
vista de la importacin, tenemos disponible la funcin get_value del

50
Las funciones y mdulos

mdulo proplists de modo que no tengamos que llamarla de forma


1
fully qualified .

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.

En este caso, vemos que si la funcin es llamada con dos parmetros, se


ejecutara la primera forma, ya que casa con el nmero de parmetros, y
en cambio, si pasamos tres parmetros, se ejecutara la segunda forma.

En Erlang sin embargo este concepto se puede completar agregando la


caracterstica de la simple asignacin y la concordancia, de modo que
nos permite hacer algo como lo siguiente:

area(cuadrado, Base) ->


Base * Base;
area(circulo, Radio) ->
math:pi() * Radio * Radio.

area(rectangulo, Base, Altura) ->


Base * Altura;
area(triangulo, Base, Altura) ->
Base * Altura / 2.

Cada funcin anterior nos retorna un rea, dependiendo del nmero


de argumentos pero adems del contenido del primer parmetro.
Gracias a ello, podemos tener funciones con el mismo nmero de
parmetros y diferente comportamiento. Como se puede observar, el
1
Fully Qualified, deriviado de su uso en los nombres DNS como FQDN, resea la llamada a una funcin
empleando toda la ruta completa para poder localizarlo, es decir, empleando tambin el mdulo.

51
Las funciones y mdulos

primer parmetro puede contener los valores: cuadrado, rectangulo,


triangulo o circulo (sin acentuar, ya que son tomos). En caso de
recibir, por ejemplo cubo, el sistema lanzara una excepcin al no poder
satisfacer la ejecucin solicitada.

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.

Por ejemplo, si queremos, del ejemplo anterior del clculo de reas,


asegurarnos de que los datos de entrada son numricos, podramos
reescribir el cdigo anterior de la siguiente forma:

area(cuadrado, Base) when is_number(Base) ->


Base * Base;
area(circulo, Radio) when is_number(Radio) ->
math:pi() * Radio * Radio.

area(rectangulo, Base, Altura)


when is_number(Base), is_number(Altura) ->
Base * Altura;
area(triangulo, Base, Altura)
when is_number(Base), is_number(Altura) ->
Base * Altura / 2.

Con esto agregamos un nivel ms de validacin, asegurndonos de que


las entradas de las variables sean numricas o en caso contrario que no
se ejecutara esa funcin. Podramos agregar en las condiciones que la
Base sea mayor de 0, al igual que la Altura y Radio, y cualesquiera otras
comprobaciones ms que se nos puedieran ocurrir.

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

Se llama clausura (en ingls clousure) a una funcin junto a un entorno


referenciado de variables no locales. Esto quiere decir que la funcin
tiene acceso a las variables del entorno en el que es definida como
si fuesen globales. Por ejemplo, si definimos una funcin calculadora
dentro de otra funcin llamada factoria, si en esta ltima funcin hay
definida una variable llamada contador, esta variable ser accesible
tambin por calculadora.

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.

Por ltimo, las funciones annimas no son ms que funciones que no


se declaran con un nombre sino que son declaradas y almacenadas en
una variable, de modo que la variable es empleada para hacer llamadas a
otras funciones, pudiendo ser pasada como parmetro o retornada como
resultado, ya que en s, es tratada como un dato.

Las clausuras de Erlang se basan en todas estas premisas. Son funciones


que, al definirse, pueden tomar el valor de las variables del entorno en el
que son definidas (ya que las variables son de simple asignacin y toman
su valor en ese momento), que cumplen con la adaptacin del clculo
lambda de Church y Kleen y son annimas puesto que su definicin
es como una instanciacin que se almacena en una variable y puede
ser enviada como parmetro, retornada como valor y adems de esto,
empleada como una funcin.

Se pueden escribir estas clausuras de la siguiente forma:

> A = 2. % dato de entorno


> F = fun(X) -> X * A end.
#Fun<erl_eval.6.111823515>
> F(5).
10

En este ejemplo a la variable F se le asigna la definicin de la clausura,


introduciendo dentro de su contexto el uso de una variable del entorno
en el que est siendo definida, en este caso la variable A. De este
modo al ejecutar la funcin F, multiplica la variable que se le pasa como
parmetro por la que tiene contenida.

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]).

multiplicador(X) when is_integer(X) ->


fun(Y) -> X * Y end.

Emplearamos este cdigo desde la consola de la siguiente forma:

> Dos = clausura:multiplicador(2), Dos(3).


6
> F = fun(X) when is_integer(X) ->
> fun(Y) -> X * Y end
> end.
#Fun<erl_eval.6.111823515>
> MDos = F(2).
#Fun<erl_eval.6.111823515>
> MDos(3).
6

Como se puede apreciar, no slo se permite generar una clausura dentro


de otra, sino que la generacin de las clausuras puede tener tambin
guardas. Si quisiramos agregar una clausura ms al cdigo, para truncar
el valor de un nmero en coma flotante en caso de que llegase como X,
podramos hacer lo siguiente:

> F = fun(X) when is_integer(X) ->


fun(Y) -> X * Y end;
(X) when is_float(X) ->
fun(Y) -> trunc(X) * Y end
end.

As conseguiremos que el tratamiento de las clausuras se tome de la


misma forma, tanto si se enva un dato de tipo entero como si el dato es
de tipo real (o en coma flotante).

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.

Esta declaracin nos permitira uilizar format/1 como una


clausura ms empleando directamente F. Esto viene muy bien
para cuando se tienen varias funciones para trabajar de una cierta
forma y se desea pasar la funcin elegida como parmetro a un
cdigo donde se emplear.

Por ltimo, voy a comentar el uso de las clausuras en la evaluacin


perezosa. Pongamos un ejemplo. Si en un momento dado queremos

54
Las funciones y mdulos

trabajar con una lista de infinitos trminos, o incluso con un contenido


que no queremos que est siempre presente, sino que se vaya
generando a medida que se necesita, podemos realizar una clausura que
haga algo como lo siguiente:

-module(infinitos).
-compile([export_all]).

enteros(Desde) ->
fun() ->
[Desde|enteros(Desde+1)]
end.

Desde consola, podramos emplear algo como lo siguiente:

> E = infinitos:enteros(5).
#Fun<infinitos.0.16233373>
> [N|F] = E().
[5|#Fun<infinitos.0.16233373>]
> [M|G] = F().
[6|#Fun<infinitos.0.16233373>]

Aunque hemos creado una recursividad infinita (algo parecido a un bucle


infinito), gracias a la evaluacin perezosa de Erlang cada nmero se
va generando a medida que vamos avanzando. Retomaremos este uso
cuando tratemos el tema de la recursividad.

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.

Estas funciones realizan un tratamiento de datos como podra hacerlo


un bucle en los lenguajes imperativos. En realidad termina siendo ms
potente ya que, debido a su naturaleza, se puede paralelizar.

A travs del uso de clausuras, podemos hacer que se aplique un cdigo


especfico a cada elemento de una lista de elementos. Veamos la lista de
funciones ms importantes de este tipo que provee Erlang:

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

> lists:map(fun(X) -> X * 2 end, L).


[2,4,6,8]

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 o r) indica desde donde se inicia la toma de elementos de la


lista. Left o izquierda sera desde la cabeza hasta la cola, y right o
derecha empezara a tomar elementos por el final de la lista hasta
el principio. A la funcin se le pasan tres parmetros, el primero es
la clausura, el segundo el valor inicial y el tercero la lista a procesar:

> 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]}

Estas son las principales funciones que pertenecen al mdulo lists.


La mayora de estas funciones ya han sido agregadas a lenguajes
imperativos, al igual que las listas de comprensin, por lo que es posible
que muchas de ellas sean ya conocidas para el lector.

Es bueno conocer estas funciones para que cuando surja la necesidad


de resolucin de un problema se pueda recurrir a ellas si es posible. Si
ests interesado en saber ms acerca de estas funciones, puedes echar
un vistazo al mdulo lists y as ampliar tu vocabulario en Erlang.

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.

La diferencia entre la recursividad y realizar un cdigo iterativo, es que


las variables locales que se emplean, en el caso de la recursividad, son
propias para cada ejecucin aislada del problema. El lazo comn entre
cada solucin o ejecucin de la funcin, son los parmetros de entrada y
los parmetros de salida, el resto se almacena en variables locales, que
en la mayora de lenguajes se almacena en una pila de ejecucin.

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.

El ejemplo ms simple de recursividad es la operacin de factorial:

-module(fact).
-compile(export_all).

fact(0) -> 1;
fact(X) -> X * fact(X-1).

En esta functin, tenemos dos casos diferenciados. El caso particular


representado por la primera declaracin de funcin, porque sabemos
que el factorial de cero es uno. Tambin disponemos del caso general,
que seran el resto de casos para una variable X lo que se resuelven
multiplicando cada valor por su anterior hasta llegar a cero.

Un tipo de algoritmos que se puede implementar muy fcilmente con


recursin son los de divide y vencers. Estos algoritmos se basan en la
divisin del problema en subproblemas ms pequeos pero similares
llegando a los casos particulares. Se resuelve cada pequeo problema
de forma aislada y despus se combinan las soluciones (si es necesario),
para conseguir la solucin global del problema.

Las tres partes que se pueden diferenciar en este algoritmo son:


separacin, recursin y combinacin. Podemos ver algunos algoritmos
clsicos como los de ordenacin de listas que nos pueden ayudar a
comprender mejor cmo funciona la recursividad.

7.1. Ordenacin por mezcla (mergesort)


Comenzaremos viendo el algoritmo de ordenacin por mezcla (o
mergesort), que se basa en hacer una particin de los elementos simple,
una recursividad sobre cada parte para descomponer el problema lo ms
que se pueda y una mezcla en la que se va realizando combinacin de
las partes ordenadas. Este algoritmo es simple en las dos primeras partes
y deja la complejidad para la tercera. Primero partimos la lista en trozos
de tamao similar, idealmente igual:

> 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

Esto lo podemos dejar dentro de una funcin que se llame separa/1


para semantizar el cdigo y diferenciarla dentro del algoritmo. La mezcla
podemos hacerla a travs de recursin tambin, de modo que, dadas dos
listas ordenadas podramos definirla as:

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.

La mezcla la realizamos tomando en cada paso de los datos de cabecera


de las listas, el que cumpla con la condicin indicada (el que sea menor),
concatenando el elemento y llamando a la funcin con los elementos
restantes. Para que este algoritmo funcione ambas listas deben de estar
ordenadas, por lo que hay que ir separando elementos hasta llegar
al caso particular, que ser la comparacin de un elemento con otro
elemento (uno con uno). Para conseguir esto, realizamos la siguiente
recursin:

ordena([]) ->
[];
ordena([H]) ->
[H];
ordena(L) ->
{L1,L2} = separa(L),
mezcla(ordena(L1), ordena(L2)).

Como puedes observar, antes de llamar a la mezcla, para cada sublista,


se vuelve a llamar a la funcin ordena/1, con lo que llega hasta la
comparacin de un slo elemento con otro. Despus un nivel ms alto de
dos con dos, tres con tres, y as hasta poder comparar la mitad de la lista
con la otra mitad para acabar con la ordenacin de la lista de nmeros.

Como dijimos al principio, la complejidad se presenta en la combinacin,


o funcin mezcla/2, que de forma recursiva se encarga de comparar
los elementos de una lista con la otra para conformar una sola en la que
estn todos ordenados.

El cdigo completo del algoritmo es el siguiente:

-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)).

Hemos dejado exportada solamente la funcin ordena/1, de modo que


para poder emplear el algoritmo habra que hacerlo as:

> mergesort:ordena([1,7,5,3,6,2]).
[1,2,3,5,6,7]

7.2. Ordenacin rpida (quicksort)


En este ejemplo, vamos a llevarnos la complejidad de la parte de
combinacin a la parte de separacin. Esta funcin, que se llama
quicksort por lo rpida que es ordenando elementos, se basa en la
ordenacin primaria de las listas para que la mezcla sea trivial.

Este algoritmo se basa en coger un elemento de la lista como pivote y


separar la lista en dos sublistas, una con los elementos menores al pivote
(la primera) y la otra con los elementos mayores (la segunda), para volver
a llamar al algoritmo para cada sublista.

Esta parte de cdigo la simplificaremos empleando listas de


comprensin, de modo que podemos hacer lo siguiente:

> [Pivote|T] = [5,2,6,4,3,2,1],


> Menor = [ X || X <- T, X =< Pivote ],
> Mayor = [ X || X <- T, X > Pivote ],
> {Menor, [Pivote|Mayor]}.
{[2,4,3,2,1],[5,6]}

La parte de la mezcla es trivial puesto que se recibirn listas ya ordenadas


como parmetros. La mezcla consiste slo en concatenar las sublistas y
retornar el resultado. La parte de la recursividad, es muy parecida a la de
mergesort. Viendo el cdigo al completo:

-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}.

mezcla(L1, L2) ->


L1 ++ L2.

ordena([]) ->
[];
ordena([H]) ->
[H];
ordena(L) ->
{L1, [Pivote], L2} = separa(L),
mezcla(ordena(L1) ++ [Pivote], ordena(L2)).

Se puede ver que la estrategia de divide y vencers se mantiene.


Por un lado separamos la lista en dos sublistas seleccionando un
pivote, retornando ambas sublistas y el pivote. Las sublistas se ordenan
mediante recursin sobre cada sublista por separado.

La ejecucin de este cdigo sera as:

> 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:

> erlang:'+'(2, 3).


5

Estas funciones reciben el nombre de BIF (en ingls Built-In Functions).


Otros ejemplos de BIFs son el clculo de MD5 (md5/1), el redondeo de
nmeros (round/1) y el clculo de la fecha (date/0) o la hora (time/0).

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

Una de las grandes fortalezas de la plataforma de Erlang es la gestin de


procesos. Los procesos en Erlang son propios de la mquina virtual y en
cada plataforma tienen las mismas caractersticas y se comportan de la
misma forma. En definitiva, no se emplean los mecanismos propios del
sistema operativo para ello sino que es la propia mquina virtual quien
provee los mecanismos para su gestin.

Para comenzar analizaremos la anatoma de un proceso en Erlang


para comprender para lo que es, los mecanismos de comunicacin de
que dispone y sus caractersticas de monitorizacin y enlazado con
otros procesos. Muchas de estas caractersticas estn presentes en los
procesos nativos de sistemas operativos como Unix o derivados (BSD,
Linux, Solaris, ...) y otras se pueden desarrollar sin estar a priori integradas
dentro del proceso.

Repasaremos tambin las ventajas e inconvenientes que tienen los


procesos de Erlang. Su estructura aporta ventajas como la posibilidad
de lanzar millones de procesos por nodo, teniendo en cuenta que
cada mquina puede ejecutar ms de un nodo. Tambin presenta
inconvenientes como la velocidad de procesamiento frente a los
procesos nativos del sistema operativo.

Por ltimo, el sistema de comparticin de informacin entre procesos


programados para la concurrencia emplea el paso de mensajes en lugar
de emplear mecanismos como la memoria compartida y semforos, o
monitores. Para ello proporciona a cada proceso un buzn y la capacidad
de enviar mensajes a otros procesos a travs de la sintaxis del propio
lenguaje, de una forma simple.

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

reproducirse (generar otros procesos) y morir (terminar su ejecucin).


El planificador de procesos de la mquina virtual de Erlang se encarga
de dar paso a cada proceso a su debido tiempo y de aprovechar los
recursos propios de la mquina, como son los procesadores disponibles,
para intentar paralelizar y optimizar al mximo posible la ejecucin de
los procesos. Esta sera la vida til de un proceso.

En Erlang el proceso es adems un animal social. Tiene mecanismos que


le permiten comunicarse con el resto de procesos y enlazarse a otros
procesos de forma vital o informativa. En caso de que un proceso muera
(ya sea debido a un fallo o porque ya no haya ms cdigo que ejecutar),
el proceso que est enlazado con l de forma vital muere tambin,
mientras que el que est enlazado de forma informativa es notificado de
su muerte.

Para esta comunicacin, el proceso dispone de un buzn. En este buzn


otros procesos pueden dejar mensajes encolados, de modo que el
proceso puede procesar estos mensajes en cualquier momento. El envo
de estos mensajes no slo se puede realizar de forma local, dentro del
mismo nodo, sino que tambin es posible entre distintos nodos que
estn interconectados entre s, ya sea dentro de la misma mquina o en
la misma red.

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:

X es el nmero del nodo, siendo cero el nodo local.

Y son los primeros 15 bits del nmero del proceso, un ndice a


la tabla de procesos.

Z son los bits 16 a 18 del nmero del proceso.

El hecho de que los valores Y y Z estn representados como dos


valores aparte, viene de las versiones R9B y anteriores, donde Y
era de 15 bits y Z era un contador de reutilizacin. Actualmente
Y y Z se siguen representando de forma separada para no romper
esa compatibilidad.

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

apartado concretaremos esas ventajas e inconvenientes para saber


manejarlos y conocer las limitaciones y las potencias que proporcionan.

Desde el principio hemos remarcado siempre que una de las potencias


de Erlang son sus procesos, y es porque me atrevera a decir que es
el nico lenguaje que dispone de una mquina virtual sobre la que
se emplean procesos propios de la mquina virtual y no del sistema
operativo. Esto confiere las siguientes ventajas:

La limitacin de procesos lanzados se amplia.


La mayora de sistemas operativos que se basan en procesos o
hilos limitan su lanzamiento a unos 64 mil aproximadamente. La
mquina virtual de Erlang gestiona la planificacin de los procesos
1
en ejecucin y eleva ese lmite a 2 millones .

La comunicacin entre procesos es ms simple y ms nutrida.


La programacin concurrente se basa la comparticin de datos, bien
mediante mecanismos como la memoria compartida y el bloqueo
de la misma a travs de semforos, o bien mediante la existencia de
secciones crticas de cdigo que manipulan los datos compartidos
a travs de monitores. Erlang sin embargo emplea el paso de
mensajes. Existe un buzn en cada proceso al que se le puede
enviar informacin (cualquier dato) y el cdigo del proceso puede
trabajar con ese dato de cualquier forma que necesite.

Son procesos y no hilos.


Cada proceso tiene su propia memoria y por tanto no comparte nada
con el resto de procesos. La ventaja principal de tener espacios
de memoria exclusiva es que cuando un proceso falla y deja su
memoria inconsistente, este hecho no afecta al resto de procesos
que pueden seguir trabajando con normalidad. Si el proceso vuelve
a levantarse y queda operativo el sistema se autorecupera del error.
En el caso de hilos, es posible que un fallo en la memoria (que s es
compartida) afecte a ms de un hilo, e incluso al programa entero.

No obstante, no todo es perfecto y siempre hay inconvenientes en las


ventajas que se pintan. Por un lado, el hecho de que la mquina virtual de
Erlang se encargue de los procesos y del planificador de procesos, tiene
su coste. Aunque BEAM est bastante optimizada y el rendimiento de la
mquina se ha ido incrementando en cada versin liberada de Erlang,
cualquier lenguaje que emplee directamente los procesos nativos del
sistema operativo ser ms rpido.

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.

La identificacin de la funcin, pasada como parmetro a spawn/1


puede realizarse de varias formas distintas. Se puede emplear una
clausura o indicar, a travs de una tripleta de datos (mdulo, funcin y
argumentos), la funcin que se ejecutar.

Las opciones que acepta spawn/1 se refieren sobretodo al nodo Erlang


en el que se lanza el proceso y al cdigo para ser ejecutado. La primera
parte la veremos un poco ms adelante. Ahora nos centraremos en el
lanzamiento del cdigo en el nodo actual.

Por ejemplo, si quisiramos ejecutar en un proceso separado la


impresin de un dato por pantalla, podramos ejecutar lo siguiente:

> spawn(io, format, ["hola mundo!"]).

Podramos hacer lo mismo en forma de clausura, obteniendo el mismo


resultado:

> spawn(fun() -> io:format("hola mundo!") end).

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:

> Pid = spawn(fun() -> io:format("hola mundo!") end).


hola mundo!<0.38.0>
> is_process_alive(Pid).
false

Como dijimos en su definicin un proceso se mantiene vivo mientras


tiene cdigo que ejecutar. Obviamente, la llamada a la funcin format/1
termina en el momento en el que imprime por pantalla el texto que se
le pasa como parmetro, por lo tanto, el proceso nuevo finaliza en ese
momento.

Si el cdigo se demorase ms tiempo en ejecutarse, la funcin


is_process_alive/1 devolvera un resultado diferente.
2
PID, siglas de Process ID o Identificador de Proceso.

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.

El registro de los nombres de procesos se realiza a travs de otra funcin


interna llamada register/2. Esta funcin se encarga de realizar la
asignacin entre el nombre del proceso y el PID para que a partir de ese
momento el sistema pueda emplear el nombre como identificador del
proceso.

El nombre debe de suministrarse como tomo, y cuando se emplee, debe


de ser tambin como tomo. Un ejemplo de esto sera el siguiente:

> Pid = spawn(fun() -> timer:sleep(100000) end).


<0.53.0>
> register(timer, Pid).
true

5. Comunicacin entre Procesos


Una vez que sabemos como lanzar procesos y bautizarlos para poder
localizarlos sin necesidad de conocer su identificador de proceso,
veamos cmo establecer una comunicacin entre procesos. Esta sera la
faceta social de nuestros procesos.

Para que un proceso pueda recibir un mensaje debe permanecer en


escucha. Esto quiere decir que debe de mantenerse en un estado
especial, en el que se toman los mensajes recibidos en el buzn del
proceso o en caso de que est vaco espera hasta la llegada de un nuevo
mensaje. El comando que realiza esta labor es receive. Tiene una sintaxis
anloga a case con alguna salvedad. En este ejemplo se puede observar
la sintaxis que presenta receive:

> receive
> Dato -> io:format("recibido: ~p~n", [Dato]
> end.

Si ejecutamos esto en la consola, veremos que se queda bloqueada. Esto


ocurre porque el proceso est a la espera de recibir un mensaje de otro
proceso. La consola de Erlang es tambin un proceso Erlang en s, si
escribisemos self/0 obtendramos su PID.

68
Procesos

El envo de un mensaje desde otro proceso se realiza a travs de una


construccin simple del lenguaje. Vamos a probar con un el siguiente
cdigo:

> Pid = spawn(fun() ->


> receive Any ->
> io:format("recibido: ~p~n", [Any])
> end
> end).
<0.49.0>
> Pid ! "hola".
recibido: "hola"
"hola"

El smbolo de exclamacin se emplea para decirle a Erlang que enve


al PID que se especifica a la izquierda del signo la informacin de la
derecha. La informacin enviada puede ser de cualquier tipo, ya sea un
tomo, una lista, un registro o una tupla con la complejidad interna que
se desee.

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.

Para poder realizar una comunicacin bidireccional, el envo debe de


agregar el PID de quin enva el mensaje. Si queremos como prueba
enviar informacin y recibir una respuesta podemos realizar lo siguiente:

> Pid = spawn(fun() ->


> receive
> {P,M} ->
> io:format("recibido: ~p~n", [M]),
> P ! "adios"
> end
> end).
<0.40.0>
> Pid ! {self(), "hola"},
> receive
> Msg ->
> io:format("retorno: ~p~n", [Msg])
> end.
recibido: "hola"
retorno: "adios"

Con este cdigo, el proceso hijo creado con spawn/1 se mantiene a la


escucha desde el momento de su nacimiento. Cuando recibe una tupla
con la forma {P,M}, imprime el mensaje M por pantalla y enva el mensaje
adios al proceso P.

69
Procesos

El proceso de la consola es quien se encarga de realizar el envo del


primer mensaje hacia el proceso con identificador Pid agregando su
propio identificador (obtenido mediante la funcin self/0) a la llamada.
A continuacin se mantiene a la escucha de la respuesta que le enva el
proceso hijo, en este caso adios.

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.

Como opcin de salida para evitar posibles bloqueos en caso de que


un evento nunca llegue, o nunca concuerde, o si simplemente se quiere
escuchar durante un cierto perodo de tiempo, podemos emplear la
seccin especial after. En esta seccin podemos indicarle al sistema un
nmero de milisegundos a esperar antes de cesar la escucha, pudiendo
indicar un cdigo especfico en este caso.

Si por ejemplo, en el cdigo anterior, queremos que el proceso que


lanzamos se mantenga slo un segundo en escucha y si no le llega
ningn mensaje finalice indicando este hecho, podemos reescribirlo de
la siguiente forma:

> Pid = spawn(fun() ->


> receive
> {P,M} ->
> io:format("recibido: ~p~n", [M]),
> P ! "adios"
> after 1000 ->
> io:format("tiempo de espera agotado~n")
> end
> end).
<0.47.0>
tiempo de espera agotado

Si ponemos ms segundos y realizamos el envo del mensaje antes de


que finalice este perodo, el comportamiento es exactamente igual al
anterior. Si dejamos el tiempo pasar, el proceso finalizar su ejecucin
informando por pantalla que el tiempo se ha agotado.

Desarrollado en forma de mdulo, para aprovechar la recursividad y que


el proceso se mantenga siempre activo, podramos hacerlo as:

-module(escucha).
-compile([export_all]).

escucha() ->
receive
{Desde, Mensaje} ->

70
Procesos

io:format("recibido: ~p~n", [Mensaje]),


Desde ! ok,
escucha();
stop ->
io:format("proceso terminado~n")
after 5000 ->
io:format("dime algo!~n"),
escucha()
end.

para(Pid) ->
Pid ! stop,
ok.

dime(Pid, Algo) ->


Pid ! {self(), Algo},
ok.

init() ->
spawn(escucha, escucha, []).

La funcin escucha/0 (del mdulo homnimo) se mantiene a la espera


de mensajes. Acepta dos tipos de mensajes. Por un lado el que ya
habamos visto antes, una tupla {proceso, mensaje} que recibir desde
otro proceso que se comunica con ste (se presentar por pantalla). El
otro tipo es un simple mensaje de stop. Cuando se recibe, como ya no
volvemos a ejecutar la funcin de escucha/0, el proceso finaliza su
ejecucin.

Adems, cada 5 segundos desde el ltimo mensaje enviado, o desde el


ltimo tiempo agotado, o desde el inicio de la ejecucin, se imprime el
mensaje dime algo!, ejecutando recursivamente la funcin escucha/0
para seguir con el proceso activo.

El cdigo para utilizar este mdulo podra ser algo como:

> Pid = escucha:init().


<0.34.0>
dime algo!
> escucha:dime(Pid, "hola").
recibido: "hola"
ok
dime algo!
> escucha:dime(Pid, "hola a todos").
recibido: "hola a todos"
ok
dime algo!
> escucha:para(Pid).
proceso terminado

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

en este lenguaje. A continuacin iremos ampliando y matizando an ms


lo visto en este apartado.

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.

Completando el ejemplo anterior, si el cdigo contuviera un fallo (no


de compilacin, sino de ejecucin), el proceso lanzado morira pero al
proceso lanzador no le sucedera absolutamente nada.

El siguiente fragmento de cdigo contiene un error:

> Pid = spawn(fun() -> A = 5, case A of 6 -> no end end).


<0.39.0>
=ERROR REPORT==== 27-Apr-2012::19:10:51 ===
Error in process <0.39.0> with exit value: ...

El error aparece en la consola provocando que el proceso termine


inmediatamente. Al proceso principal, el de la consola, no le sucede
absolutamente nada. Ni tan siquiera se entera, ya que el proceso fue
lanzado sin vinculacin.

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.

Cambiando spawn/1 por spawn_link/1 el lanzamiento del proceso se


realiza con vinculacin, produciendo:

> Pid = spawn_link(fun() -> A = 5, case A of 6 -> no end end).


<0.42.0>
=ERROR REPORT==== 27-Apr-2012::19:10:51 ===
Error in process <0.39.0> with exit value: ...

** exception exit: {case_clause,5}

Vamos a hacer un ejemplo ms completo en un mdulo. Tenemos dos


procesos que se mantienen a la escucha por un tiempo limitado y uno de
ellos en su cdigo tiene un error. En este caso ambos procesos, aunque
independientes, finalizarn, ya que uno depende del otro (as se indica
al lanzarlos enlazados).

72
Procesos

El cdigo sera as:

-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).

Al ejecutar la funcin lanza/0, se genera un nuevo proceso


independiente (sin enlazar). Este proceso a su vez genera otro enlazado
que ejecuta la funcin zipi/1. Despus se mantiene ejecutando la
funcin zape/1. Tendramos pues tres procesos: el de la consola
generado por la llamada a lanza/0, el proceso que ejecuta zipi/1 y el
proceso que ejecuta zape/1; todos ellos enlazados.

Revisando zape/1, podemos ver que cuando el contador llegue a 5,


no habr concordancia posible en la sentencia case lo que generar un
error que terminar con el proceso. Como est enlazado a zipi/1, este
proceso tambin finalizar su ejecucin.

Visto desde la consola:

> 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

=ERROR REPORT==== 30-Oct-2012::22:21:58 ===


Error in process <0.34.0> with exit value: ...

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.

Los enlaces se puede establecer o eliminar a travs de las funciones


link/1 y unlink/1. El parmetro que esperan ambas funciones es el
PID del proceso a enlazar con el actual en el que se ejecutan.

Volviendo sobre nuestro ejemplo anterior, podemos crear un proceso


que se encargue de lanzar a los otros manteniendo un enlace con cada
uno de ellos. De este modo si uno de ellos finaliza su ejecucin el enlace
con el proceso lanzador har que ste finalice por lo que el resto de
procesos sern tambin finalizados en cascada.

El cdigo del lanzador podra crearse en un mdulo que usara la funcin


link/1 de esta forma:

-module(lanzador).
-compile([export_all]).

init() ->
spawn(lanzador, loop, []).

loop() ->
receive
{link, Pid} ->
link(Pid);
error ->
throw(error)
end,
loop().

agrega(Lanzador, Pid) ->


Lanzador ! {link, Pid},
ok.

Ahora el mdulo gemelos se simplifica de la siguiente forma:

-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).

En este caso, no hemos introducido un error en el cdigo del mdulo


gemelos_lanzador sino que el error se produce durante el procesamiento
de uno de los mensajes del lanzador. En concreto, al enviarle el
mensaje error al lanzador ste lanza una excepcin produciendo la cada
automtica de los tres procesos.

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.

Un ejemplo simple del paso de mensajes cuando un proceso finaliza se


puede ver a travs de este sencillo cdigo:

> {Pid,MonRef} = spawn_monitor(fun() -> receive


> Any ->
> io:format("recibido: ~p~n", [Any])
> end
> end).
{<0.58.0>,#Ref<0.0.0.46>}
> Pid ! "hola".
recibido: "hola"
> flush().
Shell got {'DOWN',#Ref<0.0.0.96>,process,<0.58.0>,normal}
ok

El primer proceso tiene un receive que lo mantiene en espera


hasta que le llegue un mensaje. Al enviarle hola, el proceso finaliza

75
Procesos

satisfactoriamente. La funcin spawn_monitor/1 se encarga de lanzar


el nuevo proceso y enlazarle el monitor al proceso de la consola. Cuando
ejecutamos la funcin flush/0 podemos ver los mensajes que ha
recibido la consola, entre ellos el de finalizacin del proceso lanzado
anteriormente.

Si queremos lanzar un monitor sobre un proceso ya creado tendramos


que recurrir a la funcin monitor/2. El primer parmetro de esta funcin
es siempre process y el segundo parmetro ser el PID del proceso a
monitorizar. Empleando el ejemplo anterior:

> Pid = spawn(fun() -> receive


> Any ->
> io:format("recibido: ~p~n", [Any])
> end
> end).
<0.58.0>
> monitor(process, Pid).
#Ref<0.0.0.96>
> Pid ! "hola".
recibido: "hola"
> flush().
Shell got {'DOWN',#Ref<0.0.0.96>,process,<0.58.0>,normal}
ok

El mensaje de finalizacin enviado por el proceso es una tupla que consta


de las siguientes partes:

{'DOWN', MonitorRef, process, Pid, Reason}

La referencia, MonitorRef, es la misma que retorna la funcin monitor/2,


el Pid se refiere al identificador del proceso que se est monitorizando
y Reason es la razn de terminacin. Si la razn es normal es que el
proceso ha finalizado de forma correcta, en caso contrario, ser debido
a que encontr algn fallo.

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

Pid = lanza(Name, Fun),


From ! {ok, Name},
loop([{Pid,[Name, Fun]}|State]);
{'DOWN',_Ref,process,Pid,_Reason} ->
[Name, Fun] = proplists:get_value(Pid, State),
NewPid = lanza(Name, Fun),
io:format("reavivando hijo en ~p~n", [NewPid]),
AntiguoHijo = {Pid,[Name,Fun]},
NuevoHijo = {NewPid,[Name,Fun]},
loop([NuevoHijo|State] -- [AntiguoHijo])
end.

lanza(Name, Fun) ->


Pid = spawn(Fun),
register(Name, Pid),
monitor(process, Pid),
Pid.

agrega(Name, Fun) ->


monitor ! {monitor, self(), Name, Fun},
receive {ok, Pid} -> Pid end.

Como ejemplo, podemos utilizar este cdigo en consola de la siguiente


forma:

> 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>

El cdigo presente en la clausura no mantiene ningn bucle. Cuando


recibe un mensaje se ejecuta presentando por pantalla el texto Hola ...!
y finaliza. El proceso monitor recibe la salida del proceso y vuelve a
lanzarlo de nuevo, tal y como se observa en los mensajes reavivando hijo
en ....

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

Quizs sea un poco extrao encontrar este tema en un captulo dedicado


a procesos, pero nos parece apropiado ya que la recarga de cdigo
afecta directamente a los procesos. La recarga de cdigo afecta ms a un
proceso que lo emplea de forma continua (como es el cdigo base del
proceso), que a otro que lo emplea de forma eventual (funciones aisladas
que se emplean en muchos sitios).

Pondremos un ejemplo. Teniendo este cdigo:

-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().

Desde una consola podemos compilar y ejecutar el cdigo como de


costumbre:

> c(prueba).
{ok,prueba}
> Pid = spawn(prueba, code_change, []).
<0.39.0>
> Pid ! "hola", ok.
original: "hola"
ok

Se genera un proceso que mantiene el cdigo de loop/0 en ejecucin y


atiende a cada peticin que se le enva al proceso. La funcin loop/0 a
su vez llama, de forma fully qualified, a la funcin code_change/0. Esta
forma de llamar a la funcin le permite a la mquina virtual de Erlang
revisar si hay una nueva versin del cdigo en el fichero BEAM y, en caso
de ser as, recargarla.

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

Si cambiamos el cdigo del listado anterior por lo siguiente:

loop() ->
receive Any -> io:format("cambio: ~p~n", [Any]) end,
prueba:code_change().

Vamos a la consola de nuevo y recompilamos:

> c(prueba).
{ok,prueba}
> Pid ! "hola", ok.
original: "hola"
ok
> Pid ! "hola", ok.
cambio: "hola"
ok

Dado que el proceso est ya en ejecucin, hasta que no provocamos


una segunda ejecucin no se ha producido la recarga del cdigo ni
comenzado a ejecutar el nuevo cdigo.

Es bueno saber que podemos hacer que la recarga de cdigo se haga


bajo demanda, utilizando las funciones adecuadas:

-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.

Para probar este ejemplo lo lanzamos como la primera vez, haciendo


una llamada. Despus cambiamos el cdigo modificando el texto que
imprime por pantalla el mensaje y lo compilamos con la orden erlc.

Una vez hecho esto podemos provocar la recarga del cdig enviando el
mensaje update desde consola fcilmente:

> Pid ! update.


update
> Pid ! "hola", ok.
cambio: "hola"
ok

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.

En este apartado revisaremos las caractersticas de que disponemos para


la administracin de procesos dentro de un programa. Aunque ya hemos
visto muchas de estas caractersticas como la creacin, vinculacin
y monitorizacin, nos quedan otras como el listado, comprobacin y
eliminacin.

Comenzaremos por lo ms bsico, la eliminacin. Erlang nos provee


de una funcin llamada exit/2 que nos permite enviar mensajes
de terminacin a los procesos. Los procesos aceptan estas seales y
finalizan su ejecucin. El primer parmetro es el PID que es el dato
que requiere exit/2 para finalizar el proceso. El segundo parmetro
es opcional y representa el motivo de la salida. Por defecto se enva el
tomo normal. Su sintaxis por tanto es:

exit(Pid, Reason).

Por otro lado processes/0 nos proporciona una lista de procesos


activos. Con process_info/1 obtenemos la informacin sobre un
proceso dado el PID e incluso mediante process_info/2 con
un parmetro que indica la informacin especfica de la lista de
3
propiedades : enlaces con otros procesos (links), informacin de la
memoria usada por el proceso (memory), la cola de mensajes (messages),
por quin est siendo monitorizado (monitored_by) o a quin monitoriza
(monitors), el nombre del proceso (registered_name), etc.

10. Nodos Erlang


La mquina virtual de Erlang no slo tiene la capacidad de gestionar
millones de procesos en un nico nodo, o de facilitar la comunicacin
entre procesos a travs de paso de mensajes implementado a nivel
de proceso, sino que tambin facilita la comunicacin entre lo que se
conoce como nodos, dando al programador la transparencia suficiente
para que dos procesos comunicndose entre nodos diferentes se
comporten como si estuviesen dentro del mismo.
3
Toda esta informacin puede ser consultada, con mayor detalle de la siguiente direccin: http://
www.erlang.org/doc/man/erlang.html#process_info-2

80
Procesos

Cada nodo es una instancia en ejecucin de la mquina virtual de Erlang.


Esta mquina virtual posee la capacidad de poder comunicarse con otros
nodos siempre y cuando se cumplan unas caractersticas concretas:

El nodo se debe haber lanzado con un nombre de nodo vlido.

La cookie debe de ser la misma en ambos nodos.

Deben de poder conectarse, estando en la misma red.

Erlang dispone de un mecanismo de seguridad de conexin por clave,


a la que se conoce como cookie. La cookie es una palabra de paso que
permite a un nodo conectarse con otros nodos siempre que compartan
la misma cookie.

Un ejemplo de lanzamiento de un nodo Erlang, desde una terminal sera


el siguiente:

erl -sname test1 -setcookie mitest

Si lanzamos esta lnea para test1 y test2, veremos que el smobolo


de sistema de la consola de Erlang se modifica adoptando el nombre
del nodo de cada uno. En caso de que el nombre de la mquina en
la que ejecutamos esto fuese por ejemplo bosqueviejo, tendramos
dos nodos en estos momentos levantados: test1@bosqueviejo y
test2@bosqueviejo.

El nombre propio del nodo se obtiene a travs de la funcin interna


node/0. Los nodos a los que est conectado ese nodo se obtienen con
la funcin interna nodes/0. Los nodos de un cluster se obtienen con la
forma:

[node()|nodes()]

Desde la consola podemos usar el siguiente comando para conectar los


dos nodos:

(test1@bosqueviejo)> nodes().
[]
(test1@bosqueviejo)> Remoto = test2@bosqueviejo,
(test1@bosqueviejo)> net_kernel:connect_node(Remoto).
true
(test1@bosqueviejo)> nodes().
[test2@bosqueviejo]

11. Procesos Remotos


Hasta ahora, cuando emplebamos la funcin interna spawn/1
generbamos un proceso local, que se ejecutaba en el nodo que

81
Procesos

corre la funcin. Si tenemos otros nodos conectados, podemos realizar


programacin paralela o distribuida, lanzando la ejecucin de los
procesos en otros nodos. Es lo que se conoce como un proceso remoto.

Se puede lanzar un proceso remoto con la misma funcin spawn/1


agregando como primer parmetro el nombre del nodo donde queremos
lanzar el proceso. Por ejemplo, si queremos lanzar un proceso que se
mantenga a la escucha para dar informacin en el cluster montado por
los dos nodos que lanzamos en el apartado anterior, podramos hacerlo
con el siguiente cdigo:

-module(hash).
-export([init/1, get/2, set/3]).

get(Pid, Key) ->


Pid ! {get, self(), Key},
receive
Any -> Any
end.

set(Pid, Key, Value) ->


Pid ! {set, Key, Value},
ok.

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.

En la funcin init/1, se agrega el nombre del nodo que se pasa


como parmetro a spawn/2. La comunicacin la podemos realizar
normalmente como en todos los casos anteriores que hemos visto sin
problemas. No obstante, el PID devuelto, a diferencia de los vistos
anteriormente, tiene su primera parte distinta de cero lo que indica que
est corriendo en otro nodo. Los procesos en nodos remotos no se puede
registrar con la funcin interna register/2, es decir, no se les puede
asociar un nombre y por ello, son slo accesibles desde el nodo que los
lanz.

82
Procesos

12. Procesos Locales o Globales


Todos los procesos que hemos registrado hasta ahora eran locales. Si
queremos que un proceso sea accesible desde diferentes nodos debe
registrarse como proceso global.

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.

Vamos a lanzar un proceso global en un nodo:

(test1@bosqueviejo)> global:register_name(consola, self()).


yes
(test1@bosqueviejo)> receive
(test1@bosqueviejo)> Any -> io:format("~p~n", [Any])
(test1@bosqueviejo)> end.
"hola"
ok

Registramos el proceso de la consola con el nombre consola. Desde el


otro nodo de Erlang podemos enviar un mensaje de la siguiente forma:

(test2@bosqueviejo)> Remoto = test1@bosqueviejo,


(test2@bosqueviejo)> net_kernel:connect_node(Remoto).
true
(test2@bosqueviejo)> global:whereis_name(consola) ! "hola".
"hola"

El envo del mensaje lo podemos realizar a travs del PID o a travs


de la funcin send/2 del mdulo global. En todo caso, obtenemos la
capacidad de tener accesibilidad a los procesos remotos desde cualquier
nodo del cluster.

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

13. RPC: Llamada Remota a Proceso


Otra de las propiedades que tiene la mquina virtual de Erlang, es
que permite conectarse a un nodo especfico, ejecutar un comando y
obtener una respuesta. La principal diferencia con ejecutar un proceso
remotamente es que el comando RPC se lanza y se mantiene a la espera
de un retorno para esa ejecucin.

Por ejemplo, si queremos obtener el identificador de los procesos de


cada nodo conectado al cluster, podemos conectarnos a cada nodo
remoto y obtener esta informacin va RPC de la siguiente manera:

(test1@bosqueviejo)> lists:map(fun(Nodo) ->


(test1@bosqueviejo)> rpc:call(Nodo, erlang, processes, [])
(test1@bosqueviejo)> end, nodes()).
[[<6759.0.0>,<6759.3.0>,<6759.5.0>,<6759.6.0>,<6759.8.0>,
<6759.9.0>,<6759.10.0>,<6759.11.0>,<6759.12.0>,<6759.13.0>,
<6759.14.0>,<6759.15.0>,<6759.17.0>,<6759.18.0>,<6759.19.0>,
<6759.20.0>,<6759.21.0>,<6759.22.0>,<6759.23.0>,<6759.24.0>,
<6759.25.0>,<6759.26.0>,<6759.27.0>,<6759.28.0>,<6759.29.0>,
<6759.30.0>,<6759.31.0>,<6759.32.0>|...]]

Aunque el cdigo se ejecuta en el otro nodo, datos como los PID se


adaptan a la comunicacin entre nodos, por lo que podramos emplear
cualquiera de esos identificadores para obtener informacin del proceso
remoto.
6
Cualquier cdigo que se ejecute a travs de este sistema de RPC ser
ejecutado en el nodo que se indique como primer parmetro, por lo que
el cdigo debe de existir en ese nodo.

En caso de que el cdigo resida nicamente en el nodo que solicita la


ejecucin remota, existe la posibilidad de exportar el cdigo al nodo
donde queremos que se ejecute. Esto puede conseguirse con el siguiente
cdigo:

(test1@bosqueviejo)> {hash,B,F} = code:get_object_code(hash).


{hash,<<70,79,82,49,0,0,4,0,66,69,65,77,65,116,111,109,0,
0,0,126,0,0,0,17,4,104,97,...>>,
"/home/bombadil/hash.beam"}
(test1@bosqueviejo)> A = [hash, F, B].
(test1@bosqueviejo)> Host = test2@bosqueviejo,
(test1@bosqueviejo)> rpc:call(Host, code, load_binary, A).
{module,hash}

De esta forma, podramos levantar cada nuevo nodo en cualquier


mquina sin tener el cdigo. Todo quedara en llamadas RPC desde el
nodo maestro hacia los dems nodos para ir levantando instancias del
cdigo y lanzar los procesos que se requieran.
6
Son las siglas de Remote Procedure Call, o Llamada a Proceso Remoto.

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.

14. Diccionario del Proceso


Para finalizar y dar por terminado este captulo, indicar que Erlang
dispone de un diccionario de datos que puede ser empleado para
mantener datos propios del proceso. Podramos considerarlo como
atributos propios del proceso.

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.

Cuando se indica un parmetro, se toma como clave y se retorna


nicamente el valor solicitado.

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.

Este diccionario es til para poder desarrollar procesos en los que


queramos manejar atributos, para modificar o eliminar elementos del
mismo. Proporciona un depsito de datos por proceso que nos puede
ayudar a mantener informacin de estado entre llamadas a un mismo
proceso.

85
Captulo 6. ETS, DETS y Ficheros
Escribir es recordar, pero leer tambin es recordar.
Franois Mauriac

Uno de los puntos importantes en un lenguaje de programacin es


la gestin de ficheros. Los ficheros tienen innumerables usos, desde
escritura de logs, hasta el almacenamiento o lectura de datos o
configuraciones en formatos como CSV, XML o YAML, pasando por los
contenidos multimedia: imgenes en formato PNG, o vdeos de tipo AVI.
Necesitamos pues los mecanismos que nos permitan realizar todas las
operaciones con ficheros (renombrar, copiar, mover, etc.).

Erlang provee funciones bsicas y muy simplificadas de acceso a ficheros


y directorios. Nos permite realizar la lectura de un fichero en texto
plano formateado como datos de Erlang (listas, tuplas, tomos, nmeros,
etc.). Puede adems emplear tablas ETS (Erlang Term Storage) para el
almacenaje en disco o, empleando directamente DETS (Disk Erlang Term
Storage), acceder a un directorio para su procesado.

En este captulo nos adentraremos en cada aspecto referente a los


ficheros y las tablas ETS y DETS, a la lectura y escritura de ficheros de
texto y binarios, y a los mecanismos que nos da Erlang para navegar por
directorios.

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.

Por todo ello, el almacenaje de trminos Erlang constituye una


herramienta fundamental de gestin de la informacin con Erlang,
especialmente cuando el tamao de la informacin es elevado y se
necesita optimizar los tiempos de acceso.
1
A diferencia de llamadas a sistemas, como el SQL, las tablas ETS se quera que fuesen tratadas con
directivas, sentencias y funciones del lenguaje y no enviadas a un subsistema.

86
ETS, DETS y Ficheros

1.1. Tipos de Tablas


Podemos encontrar cuatro tipos de tablas ETS dependiendo de los
algoritmos empleados para la constitucin de la tabla, su almacenaje y
la extraccin de datos:

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.

Conjunto ordenado (ordered_set)


Es igual que el tipo anterior, pero en este caso los datos entrantes en
el conjunto son ordenados mediante la comparacin de su clave con
las claves de los datos almacenados a travs de los comparadores
< y >, siendo el primer elemento el ms pequeo.

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.

Bolsa duplicada (duplicate_bag)


Igual que la anterior, pero eliminando la restriccin de que las tuplas
en su conjunto y comparadas con el resto deban de ser diferentes,
es decir, se permiten tuplas repetidas (o duplicadas).

Dependiendo del tipo de datos que necesitemos almacenar en las tablas


podremos elegir uno u otro tipo de tabla. Por ejemplo, si tenemos que
almacenar trminos de forma ordenada para su extraccin podemos
emplear un ordered_set, mientras que si la informacin que queremos
almacenar puede llegar a repetirse podramos optar por alguna de las
bolsas, segn el grado de repeticin que queramos o tengamos que
permitir para los datos.

1.2. Acceso a las ETS


Las tablas ETS son creadas por un proceso que puede hacerlo con
opciones de accesibilidad que permitan su acceso por otros procesos o
no. Los parmetros de seguridad que podemos emplear para garantizar
el acceso o denegarlo, segn el caso, son los siguientes:

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).

1.3. Creacin de una ETS


Para crear una tabla ETS emplearemos la funcin new/2 del mdulo ets
cuyos parmetros son el nombre de la tabla (un tomo) y las opciones
para la creacin de la misma.

Las opciones estn en formato de lista. Cada elemento de la lista


corresponder a cada una de las siguientes secciones:

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

Siendo Pos un nmero entero dentro del rango de elementos de la


tupla.

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:

{'ETS-TRANSFER', id, FromPid, HeirData}

Y tomara en propiedad la tabla. La configuracin sera:

{heir, Pid, HeirData}

En caso de no especificar un heredero, si el proceso propietario de


la tabla termina su ejecucin la tabla desaparece con l.

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}

Activamos la concurrencia de lectura. Esta opcin es buena si


el nmero de lecturas es mayor que el de escrituras, ya que el
sistema adapta internamente los datos para que las lecturas puedan
emplear incluso los diferentes procesadores que pueda tener la
mquina.

Para activar la concurrencia en la escritura, se debe emplear la


opcin siguiente:

{write_concurrency, true}

La activacin de la escritura sigue garantizando tanto la atomicidad


como el aislamiento. Esta opcin est recomendada si el nivel de
concurrencia de lectura/escritura de los datos almacenados en la
ETS provocan excesivo tiempo de espera y fallos a consecuencia de
este cuello de botella. La nota negativa, vuelve a ser la penalizacin
existente al realizar las escrituras concurrentes.

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

de datos son ms costosos. Esta opcin es aconsejable cuando sea


ms importante el consumo de memoria que la velocidad de acceso.

Como ejemplos de creacin de ETS:

> T = ets:new(prueba, []).


16400
> ets:new(tabla, [named_table]).
tabla
> ets:new(conjunto, [set, named_table]).
conjunto
> ets:new(bolsa, [bag, named_table]).
bolsa

Todas las opciones vistas anteriormente se pueden emplear en cualquier


orden dentro de la lista de opciones, y se pueden poner tantas como se
necesite.

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:

ets:new(tabla, [set, bag, private, public])

Las opciones que predominan finalmente y las que se quedarn


configuradas son bag y public.

1.4. Lectura y Escritura en ETS


Una vez que tenemos creada una ETS podemos comenzar a trabajar con
ella. Para agregar elementos podemos emplear la funcin insert/2
cuyo primer parmetro es el identificador de la tabla (o su nombre en
caso de named_table), siendo el segundo parmetro un trmino o una
lista de trminos para su insercin.

Un ejemplo para nuestra bolsa creada anteriormente sera:

> ets:insert(bolsa, {rojo, 255, 0, 0}).


true

Con esta llamada hemos introducido un trmino en el que la clave es rojo


dentro de la bolsa. Si queremos ver el contenido de la tabla, podemos
emplear la funcin match. Esta funcin emplea dos parmetros: el
primero es el nombre de la ETS, y el segundo es el patrn que debe
de cumplir el dato para ser mostrado. De momento, daremos '$1' es el
comodn que nos permite sacar todos los datos:

90
ETS, DETS y Ficheros

> ets:match(bolsa, '$1').


[[{rojo,255,0,0}]]

Si insertamos algunos elementos ms podemos ver cmo se van


almacenando del mismo modo:

> ets:insert(bolsa, [{verde,0,255,0},{azul,0,0,255}]).


true
> ets:match(bolsa, '$1').
[[{rojo,255,0,0}],[{azul,0,0,255}],[{verde,0,255,0}]]

Los elementos se insertan donde mejor conviene al sistema interno, tal y


como se puede ver en el listado. Para extraer un elemento concreto dado
el identificador de la tupla podemos emplear la funcin lookup/2:

> ets:lookup(bolsa, azul).


[{azul,0,0,255}]

Con el uso de las funciones first/1 y next/2, o last/1 y prev/2,


podemos recorrer la lista utilizando la recursin. Si llegamos al final nos
devolver el tomo '$end_of_table'.

Un ejemplo de esto se puede ver en el siguiente mdulo:

-module(ets_show).
-compile([export_all]).

show_all(Ets) ->
show_all(Ets, ets:first(Ets), []).

show_all(_Ets, '$end_of_table', List) ->


List;
show_all(Ets, Id, List) ->
show_all(Ets,ets:next(Ets,Id),ets:lookup(Ets,Id) ++ List).

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).

Si ejecutamos la funcin main/0, veremos como nos retorna todo


lo insertado dentro de bolsa. Igualmente, si creamos una nueva ETS,
podemos emplear la funcin show_all/1 para listar todo su contenido.

1.5. Match: bsquedas avanzadas


En la seccin anterior vimos que el listado general se poda conseguir con
una forma especfica de la funcin match. Esta funcin, a travs del uso
de los patrones, nos permite mucha mayor potencia a la hora de rescatar
datos de la ETS.

91
ETS, DETS y Ficheros

La teora de la concordancia para las ETS se puede emplear tanto


para funciones select/2, como para las funciones match/2. Esta
concordancia se basa en pasarle la informacin al ncleo de ETS para
realizar la extraccin. Requiere que se puedan identificar variables como
tal o bien con el uso de comodines.

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).

El otro tipo de tomo con significado especfico para las ETS es el


comodn '_'. Ya vimos en su momento que el signo de subrayado se utiliza
para indicar que el dato en esa posicin no interesa.

Rescatando el ejemplo anterior, vemos que habamos escrito como


parmetro de la funcin match/2 la siguiente expresin:

'$1'

Al no tener $1 forma de tupla, esta expresin de la variable sin


importancia concuerda con toda la tupla al completo. Si pusiramos en
el match lo siguiente:

{'$1',255,'_','_'}

Veremos que extraemos el valor rojo, ya que es el nico que cumple la


condicin de concordancia de tener en su segunda posicin el valor 255.
Como la variable sin importancia slo la hemos situado en la primera
posicin, slo recibiremos esta.

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.

El formato que se emplea se denomina especificaciones de concordancia


y consta de una tupla de tres elementos: el primero el de la concordancia,
ya visto anteriormente, el segundo es el que almacena las guardas y el
tercero el que se encarga de especificar la proyeccin de elementos.

Comenzaremos con este ejemplo:

{
{'$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.

El segundo parmetro contiene una operacin. Como se puede observar


el formato es igual al conocido como calculadora polaca, la operacin
es el primer elemento que se encuentra y los operandos los que vienen
a continuacin. Volviendo a nuestro ejemplo concreto la condicin que
debe de cumplir la tupla es que su elemento '$1' sea mayor que cero.

Por ltimo, en el tercer elemento, realizamos una proyeccin para


devolver como resultado nico el primer elemento de la tupla
(precisamente '$0' el identificador o clave).

Otro ejemplo ms complejo o completo se deja a la investigacin del


lector:

{
{'$1','$2','$4','$8'},
[{'andalso',
{'==','$2',0},
{'==','$8',0}
}],
[ '$$' ]
}

1.6. Eliminando tuplas


Para eliminar una o varias tuplas, se utiliza la funcin delete/2.
Esta funcin permite eliminar una clave concreta de una ETS dada, un
funcionamiento muy parecido al de la funcin lookup/2.

ets:delete(bolsa, verde)

93
ETS, DETS y Ficheros

Tambin cabe la posibilidad de realizar una eliminacin completa de la


tabla con la funcin delete/1, o bien el vaciado de sus elementos con
la funcin delete_all_objects/1.

Si lo que queremos es eliminar una serie de objetos especficos lo


podemos realizar a travs de la funcin match_delete/2. Esta funcin
acepta como parmetros la ETS, y el patrn de elementos a eliminar, a
igual que en la funcin match/2 ya vista.

1.7. ETS a fichero


Una forma de leer la informacin de una ETS desde un fichero y volver
a almacenarla en un fichero es a travs de las funciones tab2file/2 y
file2tab/1.

La funcin tab2file/2 tiene la siguiente forma:

tab2file(Tab, Filename) -> ok | {error, Reason}

En el fichero que se especifica se almacena toda la ETS, con una cabecera


en la que se almacenan las opciones con las que fue creada, para que
cuando se vuelva a leer el fichero, la ETS se instancie de la misma forma
en la que se guard.

La funcin file2tab/1 tiene la siguiente forma:

file2tab(Filename) -> {ok, Tab} | {error, Reason}

Se encarga de leer el fichero, tomar la cabecera y crear la ETS con su


contenido, tal y como se guard.

El almacenamiento de archivos es un buen sistema para poder gestionar


los datos de una ETS de manera persistente. No obstante hay que tener
en mente que siempre pueden surgir problemas. Por ejemplo el tamao
de la ETS puede superar el de la memoria que se puede emplear, o bien el
sistema puede fallar antes de que se haya podido guardar la informacin
en el fichero. Incluso podra ser que el fichero se corrompiera durante
su escritura. Para evitar estos problemas necesitamos un sistema que
trabaje directamente con el fichero y que la robustez para haber previsto
este tipo de problemas. En Erlang este sistema son las DETS.

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

El sistema DETS se suele emplear cuando se requieren almacenar con


cierta persistencia informacin en forma de trminos Erlang y cuyo
fichero de almacenaje no exceda los 2 GiB de espacio.
3
Este sistema de almacenaje es el mismo que emplea Mnesia , el motor
de base de datos que integra OTP y que viene por defecto con Erlang.
Mnesia, como motor de base de datos, no slo posee la capacidad de
trabajar con trminos para su almacenaje, sino que proporciona otros
elementos como transacciones, consultas, distribucin y fragmentacin
de tablas, por lo que, puede ser un entorno ms complejo y potente que
si slo se requieren almacenar trminos.

2.1. Tipos de Tablas


Al igual que las ETS, las DETS se pueden crear de varios tipos y comparten
estos tipos con las ETS, a excepcin de los conjuntos ordenados que no
se incluyen de momento por no haber encontrado una forma ptima de
realizar su tratamiento.

Repasamos los tipos que se pueden crear para las DETS:

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.

Bolsa duplicada (duplicate_bag)


Igual que la anterior, pero eliminando la restriccin de que las tuplas
en su conjunto y comparadas con el resto deban de ser diferentes,
es decir, se permiten tuplas repetidas (o duplicadas).

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

2.2. Crear o abrir una DETS


Esta operacin se realiza mediante la funcin open_file/2. Este
comando no slo sirve para crear la DETS, sino que adems una vez que la
DETS exista, nos permite abrirla y seguir usndola en otras ejecuciones.
Veamos los parmetros de la funcin para abrir o crear una DETS:

open_file(Name, Args) -> {ok, Name} | {error, Reason}

El parmetro Name ser el que le d nombre a la DETS. El nombre debe


de ser un tomo como ocurre con las ETS. Este dato ser el que se solicite
en el resto de funciones para poder acceder a la DETS.

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).

{repair, true | false | force}


Le dice al sistema que debe ejecutar la reparacin de la DETS al
abrirla, en caso de que no se hubiese cerrado correctamente. Por
defecto est activa (true). En caso de que se indique false y se
requiera reparacin, se retornar el error:

{error, {needs_repair, File}}

El valor force quiere decir que la reparacin se llevar a cabo aunque


la tabla haya sido cerrada correctamente.

{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.

Veamos un ejemplo de apertura de un par de tablas:

> dets:open_file(bolsa, [{type, bag}, {file, "bolsa.dat"}]).


{ok,bolsa}
> dets:info(bolsa).
[{type,bag},
{keypos,1},
{size,0},
{file_size,5464},
{filename,"bolsa.dat"}]
> dets:info(bolsa, file_size).
5464
> dets:open_file(conjunto, [{type, set}, {access, read}]).
{error,{file_error,"conjunto",enoent}}

Al igual que con las ETS, las funciones info/1 e info/2, nos
proporcionan informacin sobre la DETS abierta dado su nombre.

La ltima apertura, como se puede ver, nos origina un error. Esto es


debido a que se ha intentado abrir un fichero llamado conjunto que no

97
ETS, DETS y Ficheros

existe. Como el acceso que se da es de slo lectura (read), no se le est


capacitando para crear el fichero y por tanto se origina el error.

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.

2.3. Manipulacin de las DETS


Si nos fijamos en las funciones disponibles para las DETS, veremos que
son prcticamente iguales que las que hay disponbiles para las ETS. Por
tanto, damos por hecho que el comportamiento de cara al programador
debe de ser el mismo.

Como todas las funciones son iguales y se pueden emplear funciones


como lookup/2, delete/2, delete_all_objects/1, first/1,
last/1, next/2, prev/2, ...; se puede repasar el apartado de Lectura y
Escritura de ETS, as como el de bsquedas avanzadas y la eliminacin
de tuplas.

2.4. De ETS a DETS y viceversa


Aunque ambas estructuras estn optimizadas para sus respectivos
trabajos, la manipulacin de las entidades de memoria siempre resulta
ms rpido que las de disco. Por esta razn puede haber momentos en los
que, aunque se tenga almacenado todo en una DETS por la persistencia,
se quiera por motivos de rendimiento utilizar una ETS para trabajar y a la
hora de almacenar los datos a disco, volver a trabajar con una DETS.

Para esto se utilizan las funciones to_ets/2 y from_ets/2. Su


definicin:

to_ets(Name, EtsTab) -> EtsTab | {error, Reason}

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.

La especificacin de la funcin from_ets/2 es:

from_ets(Name, EtsTab) -> ok | {error, Reason}

98
ETS, DETS y Ficheros

En este caso, la DETS s es vaciada (se eliminan todos sus elementos) y a


continuacin se inserta la ETS tal cual dentro de la DETS.

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 de texto tienen un tratamiento especial, ya que se


interpretan algunos caracteres especiales como fin de fichero, salto de
lnea, e incluso se pueden tomar ficheros formateados de cierta forma
para que se carguen como formas de datos de Erlang.

En cambio, los ficheros binarios, tienen un tamao definido y todos sus


bytes son iguales, es decir, no tienen ningn significado especial, aunque
se pueden agrupar para definir formas de datos estructuradas.

3.1. Abriendo y Cerrando Ficheros


Cualquier fichero que haya en un sistema de ficheros al que tenga acceso
Erlang es susceptible de poder ser abierto. Tambin podemos generar
nuevos ficheros dentro de ese mismo sistema de ficheros.

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.

La funcin de que disponemos para poder abrir o crear ficheros es la


siguiente:

open(Filename, Modes) -> {ok, IoDevice} | {error, Reason}

Como primer parmetro tenemos el nombre del fichero (Filename), y el


segundo parmtro corresponde a una lista en la que se pueden agregar
tantas opciones de las siguientes como se necesite:

read | write | append | exclusive


El fichero se puede abrir en modo de lectura (read), escritura (write)
o para agregacin al final (append). El caso de exclusive, es usado
para crear el fichero, devolviendo un error en caso de que ya exista.

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.

{delayed_write, Size, Delay}


Los datos se mantienen en un buffer hasta que se alcanza el tamao
indicado por Size o hasta que el dato ms antiguo en el buffer es de
ms all del tiempo especificado en Delay, entonces se escriben a
disco. Esta opcin se emplea para decrementar los accesos a disco
y, por lo tanto, intentar incrementar el rendimiento del sistema.

{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:

> file:open("/etc/debian_version", [read]).


{ok,<0.52.0>}
> file:open("/etc/debian_version", [write]).
{error,eacces}

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.

Al intentar abrir un fichero para escritura hemos obtenido un error de


acceso (eaccess) debido a la falta de permisos, ya que un usuario normal
no tiene permisos para escribir en ese fichero.

Para cerrar el fichero, y con ello liberar el proceso que se mantiene a la


espera de indicaciones para tratar dicho fichero, debemos de emplear la
funcin close/1. En los ejemplos anteriores, sera hacer lo siguiente:

> {ok, Pid} = file:open("/etc/debian_version", [read]).


{ok,<0.34.0>}
> file:close(Pid).
ok

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.

3.2. Lectura de Ficheros de Texto


Los ficheros de texto son los que el sistema interpreta dando un
significado concreto a ciertos caracteres especiales. El caracter de avance
de lnea (\n), es tomado como un salto de lnea y el caracter de retorno de
carro (\r), en caso de ir seguido al avance de lnea, es ignorado y tomado
como parte del salto de lnea.

Con esta consideracin, funciones como read_line/1, se encargan de


leer una lnea del fichero. Leen tantos bytes como sean necesarios hasta
llegar a un salto de lnea o el final del fichero.

Como el fichero que vimos en el ejemplo de apertura de ficheros es


de tipo texto, podemos ir leyendo lnea a lnea mediante la funcin
read_line/1 hasta el fin de fichero y luego cerrarlo, como se ve en el
siguiente ejemplo:
6
Son las estructuras del sistema operativo que se emplean para designar que un programa tiene un
fichero abierto.

101
ETS, DETS y Ficheros

> {ok, Pid} = file:open("/etc/motd", [read]).


{ok,<0.34.0>}
> file:read_line(Pid).
{ok,"Linux barbol 3.1.0-1-amd64 Tue Jan 10 05:01:58 UTC..."}
> file:read_line(Pid).
{ok,"\n"}
> file:read_line(Pid).
{ok,"The programs included with the Debian GNU/Linux..."}
> file:read_line(Pid).
{ok,"the exact distribution terms for each program are..."}
> file:read_line(Pid).
{ok,"individual files in /usr/share/doc/*/copyright.\n"}
> file:read_line(Pid).
{ok,"\n"}
> file:read_line(Pid).
{ok,"Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY..."}
> file:read_line(Pid).
{ok,"permitted by applicable law.\n"}
> file:read_line(Pid).
eof
> file:close(Pid).
ok

Si lo que queremos es leer todo el contenido del fichero para


almacenarlo en una variable de texto, podemos emplear la funcin
read_file/1:

> 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.

Por ltimo, si el contenido del fichero que queremos leer contiene


elementos de Erlang, como trminos o listas, separados por un punto
cada uno de los elementos base que conforman el documento, este
puede ser ledo y evaluado como datos Erlang directamente.

Esto se realiza con al funcin consult/1. Esta funcin puede leer un


fichero como el que se muestra a continuacin:

{nombre, "Manuel"}.
{apellido1, "Rubio"}.
{apellido2, "Jimenez"}.

Leyendo este fichero (datos_personales.cfg) desde consola:

> file:consult("datos_personales.cfg").

102
ETS, DETS y Ficheros

{ok,[{nombre,"Manuel"},
{apellido1,"Rubio"},
{apellido2,"Jimenez"}]}

Con esta sintaxis crearemos archivos que podamos emplear como


configuracin, parametrizacin o salvaguarda de informacin, cuyos
datos podemos rescatar en cualquier momento.

3.3. Escritura de Ficheros de Texto


La escritura de ficheros de texto la podemos realizar simplemente a
travs de las funciones format/3 o write/2. La primera funcin nos
permite formatear el texto que ser escrito en el fichero tal y como se
hara en la pantalla, teniendo en cuenta los saltos de lnea y los espacios;
la segunda nos permite escribir slo lo que contenga la lista de caracteres
que enviamos como segundo parmetro.

Si nuestro fichero debe disponer de un formato especfico (espacios,


saltos de lnea, formato de nmeros), puede ser ms fcil formatearlo a
travs de la funcin format/3, tal y como hacamos con la pantalla, por
ejemplo as:

> {ok, Pid} = file:open("mifile.txt", [write]).


{ok,<0.42.0>}
> io:format(Pid,"~nSaldo: ~6.2f~nTotal: ~6.2f~n",[12.3,20.1]).
ok
> file:close(Pid).
ok

Si por contra lo que queremos es simplemente escribir un texto ya


almacenado dentro de una cadena, podemos simplificar empleando la
funcin write/2 tal y como se ve en este ejemplo:

> {ok, Pid} = file:open("mifile.txt", [write]).


{ok,<0.42.0>}
> file:write(Pid, "fichero de texto").
ok
> file:close(Pid).
ok

Tambin es posible utilizar ambos mtodos realizando escrituras


combinadas, es decir, primero una y despus la otra, tantas veces como
queramos o necesitemos.

3.4. Lectura de Ficheros Binarios


La lectura de estos archivos la realizaremos expresamente con la funcin
read/2. Como primer parmetro emplearemos el identificador para el

103
ETS, DETS y Ficheros

fichero abierto y, como segundo parmetro, el tamao del fichero que


ser ledo.

Aunque podemos emplear otras funciones, esta es la ms genrica y nos


permitir realizar las lecturas de los ficheros sin problemas. Si queremos
leer la totalidad del fichero es ms aconsejable emplear la funcin
read_file/1, ya que nos auna la apertura, lectura y cierre del fichero
en una sola funcin.

Un ejemplo de lectura de una imagen sera la siguiente:

> Filename = "/usr/share/pixmaps/debian-logo.png".


"/usr/share/pixmaps/debian-logo.png"
> {ok, Pid} = file:open(Filename, [read,binary]).
{ok,<0.34.0>}
> file:read(Pid, 16).
{ok,<<137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82>>}
> file:close(Pid).
ok

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 ):

> Filename = "/usr/share/pixmaps/debian-logo.png".


"/usr/share/pixmaps/debian-logo.png"
> {ok, Pid} = file:open(Filename, [read,binary]).
{ok,<0.34.0>}
> {ok, <<137,"PNG",13,10,26,10>>} = file:read(Pid, 8).
{ok,<<137,80,78,71,13,10,26,10>>}
> {ok, <<Length:32, "IHDR">>} = file:read(Pid, 8).
{ok,<<0,0,0,13,73,72,68,82>>}
> {ok, <<Width:32, Height:32, Depth:8, Color:8,
> Compression:8, Filter:8, Interlace:8>>}
> = file:read(Pid, Length).
{ok,<<0,0,0,48,0,0,0,48,8,6,0,0,0>>}
> io:format("Image ~bx~b pixels~n", [Width,Height]).
Image 48x48 pixels
ok
> file:close(Pid).
ok

Siguiendo las directrices de la especificacin de los ficheros PNG, hemos


podido extraer, gracias a las listas binarias y al tratamiento que se puede
realizar con los bits, el tamao de la imagen y muchos otros datos que
podramos tambin pasar por pantalla.

Es aconsejable realizar el tratamiento de ficheros binarios siempre a


travs de listas binarias. En el ejemplo de la lectura del PNG hemos
7
http://en.wikipedia.org/wiki/Portable_Network_Graphics

104
ETS, DETS y Ficheros

visto cmo se desempaqueta el entramado de bytes para obtener la


informacin codificada en el fichero. Teniendo la definicin de otros
tipos de documentos binarios se podra hacer lo mismo para ficheros de
audio como los WAV o MP3, o para ficheros de vdeo como los AVI o MOV.

3.5. Escritura de Ficheros Binarios


La escritura de ficheros binarios se realiza con la funcin write/2 igual
que hemos hecho con los archivos de texto. La diferencia radica en que
el parmetro que se enva para ser escrito no es una lista de caracteres,
sino una lista binaria.

El ejemplo ms simple, sera la copia de un fichero. Abrimos el fichero


que queremos leer y el que queremos escribir para lectura y escritura
respectivamente. A continuacin hacemos una lectura completa del
fichero con la funcin read_file/1:

> {ok, Contenido} = file:read_file("logo.png",[read,binary]).


{ok,<<137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,
0,48,0,0,0,48,8,6,0,...>>}
2> {ok, Destino} = file:open("logo2.png", [write,binary]).
{ok,<0.35.0>}
3> file:write(Destino, Contenido).
ok
4> file:close(Destino).
ok

3.6. Acceso aleatorio de Ficheros


Hasta ahora hemos eliminado el contenido de los archivos que hemos
escrito. Si lo que quisiramos es modificar un archivo binario (en nuestro
ejemplo es modificar la cabecera PNG), la apertura del fichero debera
agregar el parmetro read adems de write, tal y como se ve en este
ejemplo:

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).

La funcin que permite realizar este tipo de movimientos por el fichero


es position/2 (del mdulo file). Esta funcin nos permite desplazarnos
por el fichero a posiciones absolutas (o relativas al principio del fichero,
tambin llamado bof, o begin of file), relativas a la posicin actual: cur; o
relativas al final del fichero: eof.

Los parmetros que podemos emplear son:

105
ETS, DETS y Ficheros

> {ok, Pid} = file:open("logo.png", [binary,write,read]).


{ok,<0.99.0>}
> file:position(Pid, 1024).
{ok,1024}
> file:position(Pid, {cur, -24}).
{ok,1000}
> file:position(Pid, eof).
{ok,1718}
> file:position(Pid, {eof, 24}).
{ok,1742}
> file:position(Pid, {eof, -24}).
{ok,1694}
> file:position(Pid, bof).
{ok,0}
> file:close(Pid).
ok

Vemos que incluso podemos desplazarnos ms all del tamao del


fichero (que en ese caso es de 1718 bytes), debido a que hemos abierto
el fichero para escritura y se nos permite agregar ms informacin para
ampliar el tamao del fichero.

3.7. Lecturas y Escrituras por Lotes


Erlang tiene muchos mecanismos para optimizar al mximo posible las
operaciones que se realicen con l. Como las lecturas aleatorias son
lentas, sobretodo si hay que llamar a la misma funcin una y otra vez con
desplazamientos del puntero del fichero, el sistema interno de Erlang
nos facilita esta labor proporcionando un par de funciones para realizar
estas lecturas o escrituras por lotes. Estas funciones son pwrite/2 y
pread/2.

Estas funciones estn pensadas para cuando tenemos la informacin


en un fichero pero al mismo tiempo en memoria. Lo que ocurre es que
trabajamos en memoria almacenando un log de cambios y los pasamos
al fichero de una vez cada cierto tiempo.

Estas anotaciones de cambios pueden ser del tipo:

[
{{bof, 0}, <<137,"PNG">>},
{{eof, -24}, <<0,0,0,0>>},
{{bof, 12}, <<"IHDR">>}
]

Con esta secuencia indicamos la posicin y lo que deseamos escribir


en cada punto. Esto se pasara como segundo parmetro en la funcin
pwrite/2.

De forma anloga, tambin se puede dar la lista de direcciones para


realizar las lecturas oportunas, es decir, para pread/2, de la siguiente
forma:

106
ETS, DETS y Ficheros

[
{{bof,0}, 4},
{{eof, -24}, 4},
{{bof, 12}, 4}
]

En este caso, en lugar de especificar contenido para escribir,


especificamos el tamao para leer a partir de la posicin dada.

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).

Para evitar estos errores, aconsejo emplear el acceso aleatorio


tan slo en ficheros binarios, ya que las unidades estn mejor
definidas en este caso.

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.

4.1. Nombre del fichero


Doy por supuesto que todos conocemos que, los nombres de los ficheros
se componen por la ruta en la que se ubica el fichero, su nombre
y extensin. En Erlang, el mdulo filename nos permite obtener la
informacin correspondiente a un nombre de fichero: su ruta, su nombre,
su nombre raz (sin extensin) y/o su extensin.

Para esto, disponemos de varias funciones:

> Filename = "/home/bombadil/logo.png".


"/home/bombadil/logo.png"
> filename:basename(Filename).
"logo.png"
> filename:rootname(Filename).
"/home/bombadil/logo"
> filename:dirname(Filename).
"/home/bombadil"
> filename:extension(Filename).

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"

Como en otros lenguajes basename/1 nos retorna el nombre del fichero


sin ruta. dirname/1 nos devuelve la ruta sin el nombre del fichero.
Tambin tenemos rootname/1 que nos retorna el nombre del fichero
(con ruta si dispone de ella) y extension/1 que nos da nicamente la
extensin (con el punto includo).

Tambin disponemos de la funcin absname/1 que retorna siempre


el nombre del fichero de forma absoluta. Si le pasamos una ruta
relativa o un fichero sin ruta, obtenemos un nombre de fichero absoluto,
completado con la ruta de trabajo actual:

> filename:absname("logo.png").
"/home/bombadil/logo.png"

4.2. Copiar, Mover y Eliminar Ficheros


Una de las acciones bsicas cuando se trata con ficheros son estas tres
que encabezan la seccin actual. Para estas acciones Erlang provee de
tres funciones: copy/2, rename/2 y delete/1.

Un ejemplo de cmo podemos emplear estas funciones:

> file:copy("logo.png", "logo2.png").


{ok,1718}
> file:rename("logo2.png", "milogo.png").
ok
> file:delete("milogo.png").
ok

La funcin de rename/2, adems de para cambiar el nombre del fichero,


nos puede servir para cambiar la ubicacin del fichero si indicamos una
ruta distinta, por ejemplo:

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.

En caso de que no queramos eliminar un fichero (porque un programa lo


tenga abierto o por otro motivo) y slo queramos eliminar su contenido

108
ETS, DETS y Ficheros

y reiniciar sus punteros a la posicin cero, esto lo podemos realizar


mediante el uso de la funcin truncate/1.

4.3. Permisos, Propietarios y Grupos


Otro de los aspectos relevantes cuando gestionamos ficheros, son sus
permisos y su pertenencia a un usuario o grupo. El cambio de los
permisos se puede realizar mediante la funcin change_mode/2. El
cambio de propietario se hace mediante change_owner/2 y el cambio
de grupo a travs de change_group/2.

La funcin change_mode/2 permite cambiar los permisos del fichero.


Como primer parmetro se pasa el nombre del fichero y como segundo
parmetro el modo que se desea establecer, en modo numrico. En modo
octal, tenemos esta tabla de permisos:

Valor numrico Permiso Usuario


8#00400 Lectura Propietario
8#00200 Escritura Propietario
8#00100 Ejecucin Propietario
8#00040 Lectura Grupo
8#00020 Escritura Grupo
8#00010 Ejecucin Grupo
8#00004 Lectura Otros
8#00002 Escritura Otros
8#00001 Ejecucin Otros

Por lo que si queremos que el fichero logo.png tenga permisos de


lectura y escritura para su propietario y lectura para el grupo y otros,
tendremos que ejecutar:

file:change_mode("logo.png", 8#00644)

El cambio de propietario se puede realizar a travs de la funcin


change_owner/2 o change_owner/3 si adems queremos cambiar el
9
grupo. Los parmetros de UID y GID se dan en formato entero .

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

todas las modificaciones en una sola accin, tanto para la lectura:


read_file_info/1; como para la escritura: write_file_info/2.
Vamos a verlas un poco ms en detalle:

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.

Los directorios nos permiten organizar nuestros ficheros de una forma


ms categorizada. Para sistemas que trabajan con miles de ficheros esto
no es una opcin sino una necesidad, ya que, en el terreno informtico
no hay recursos infinitos e incluso el nmero de ficheros que pueden
10
albergarse en un directorio est limitado .

Como la gestin de los directorios, e incluso la de los ficheros se puede


realizar desde un programa, un sistema que cree muchos ficheros puede
particionar estos en directorios y subdirectorios, de modo que el acceso
a cada directorio sea ms rpido que en el caso de tener un directorio
con miles de ficheros.
10
Hay sistemas de ficheros que establecen este lmite a 1024 y otros que permiten miles o millones
de ficheros por directorio, pero esto no es nada aconsejable, ya que el tratamiento y gestin del propio
directorio o de los propios ficheros puede ser extremadamente lento.

110
ETS, DETS y Ficheros

Veremos a continuacin las funciones relativas a la gestin de directorios


bajo los conceptos en los que se emplean.

5.1. Directorio de Trabajo


Las rutas que indicamos para los nombres de ficheros las podemos
indicar de forma absoluta o relativa tanto para su apertura como para
su gestin. La ruta absoluta nos indica dnde se encuentra un fichero
mientras que la ruta relativa se basa en la ruta activa en la que se est
trabajando.

Erlang establece una ruta de trabajo que puede ir cambiando a travs de


llamadas especficas al sistema. La ruta de trabajo podemos extraerla con
get_cwd/0 del mdulo file. Cualquier referencia a fichero que hagamos
de forma relativa ser siempre relativa a esta ruta.

Podemos cambiar la ruta de trabajo mediante la funcin set_cwd/1,


donde especificamos cul ser la nueva ruta de trabajo. Esto afectar a
todas las rutas relativas que se empleen a partir del cambio.

Es un mtodo bastante frecuente el cambiar la ruta para la ejecucin


de un cdigo especfico y volver inmediatamente a la ruta anterior, algo
como esto:

Dir = file:get_cwd(),
file:set_cwd("/miruta"),
%% ejecuta_codigo...
file:set_cwd(Dir).

La ruta inicial se sita en el directorio en el que nos encontrsemos al


ejecutar la consola de Erlang.

5.2. Creacin y Eliminacin de Directorios


Una de las acciones bsicas con los directorios es la de su creacin y
eliminacin. Comenzaremos con el primer caso, la creacin. La creacin
se puede indicar de forma absoluta o relativa. Un ejemplo:

file:make_dir("/home/bombadil/logos")

En sistemas como la shell de los sistemas tipo Unix, se permite realizar


el comando:

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

crear el ltimo directorio que se indique en la ruta, siempre que no


exista ya.

En caso de que quisiramos crear un directorio con todos sus


directorios padres, en caso de que no existan, podemos emplear la
funcin ensure_dir/1 del mdulo filelib. Esta funcin crea todos los
directorios necesarios para que la ruta exista.

Podemos ver un ejemplo:

> file:make_dir("/tmp/prueba/dir1").
{error,enoent}
> filelib:ensure_dir("/tmp/prueba/dir1/").
ok

Partimos de que /tmp/prueba no existe. Por este motivo la funcin


make_dir/1 no puede crear el directorio final, dir1. La funcin
ensure_dir/1, crea ambos directorios, primero prueba y despus dir1
dentro de prueba.

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:

temp_dir_config(Dir) when not is_dir(Dir) ->


ok = filelib:ensure_dir(Dir ++ "/"),
temp_dir_config(Dir);
temp_dir_config(Dir) ->
to_do.

Igualmente podemos emplear las funciones file_size/1 (tamao del


fichero) o last_modified/1 (la ltima fecha de modificacin) para
agregar ms semntica o funcionalidad al cdigo.

112
ETS, DETS y Ficheros

5.4. Contenido de los Directorios


Hay momentos en los que queremos tener un listado de todos los
ficheros que se encuentran dentro de un directorio, para realizar un
listado dentro del programa ya sea para tener un control del nmero de
ficheros que se van generando, para realizar bsquedas o por cualquier
otro motivo.

Una forma rpida de obtener los ficheros que queremos es emplear la


funcin list_dir/1, lo cual nos retorna una lista de todos los nombres
de ficheros que se encuentran en la ruta pasada como parmetro:

> 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"]

Con estos listados, a travs de funciones sobre listas como map/2,


podemos realizar un procesado individualizado de los ficheros que nos
retornen las funciones. Por ejemplo, si queremos extraer, adems del
nombre del fichero el tamao y mostrarlo:

> lists:map(fun(X) ->


> io:format("~-30s ~w~n", [X,filelib:file_size(X)])
> end, filelib:wildcard("/home/bombadil/*.png")).
/home/bombadil/logo.png 1718

Esto nos abre una cantidad de posibilidades para obtener informacin


de los ficheros albergados en un directorio, o incluso para poder recorrer
directorios a travs de funciones recursivas, en las que poder emplear
las guardas vistas referentes a los ficheros.

113
Captulo 7. Comunicaciones
No hay lugares remotos. En virtud de los medios de
comunicacin actuales, todo es ahora.
Herbert Marshall Mcluhan

Uno de los principales cometidos de un servidor, es establecer puertos


de comunicacin para recibir conexiones entrantes. La comunicacin
se establece a varios niveles, empleando en cada uno de los niveles
un protocolo especfico para la comunicacin. En este captulo nos
centraremos en el protocolo IP, TCP y UDP para la pila de conexiones ms
popular: TCP/IP.

1. Conceptos bsicos de Redes


Cuando se establece una conexin, la informacin que se percibe en el
ms alto nivel (o nivel de aplicacin), es una representacin que se ha
ido resolviendo de un sistema de empaquetado anterior, encargado de
agregar informacin sobre el paquete y enviarlo a travs de la red.

Las capas que se distinguen en el envo de informacin de un punto a


otro, en el modelo de Internet denominado TCP/IP, son:

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

que lleguen a su destino y los acuses de recibo para asegurar de


que el paquete es recibido correctamente. En este nivel veremos
dos protocolos: TCP y UDP.

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 .

En la expresin de direcciones IPv4 en decimal se separa cada octeto


por un punto. Cada uno de estos octetos puede estar comprendido
entre 0 y 255, salvo algunas excepciones. Los ceros iniciales, si los
hubiera, se pueden obviar. Ejemplo de representacin de direccin IP:
164.12.123.65

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}

Este sera el formato de IP para 127.0.0.1. La funcin inet:ip/1


nos permite realizar la conversin del formato de texto al formato
de tupla.

Hay tres clases de direcciones IP que una organizacin puede recibir


de parte de la Internet Corporation for Assigned Names and Numbers
(ICANN): clase A, clase B y clase C. En la actualidad, ICANN reserva
2
las direcciones de clase A para los gobiernos de todo el mundo y
las direcciones de clase B para las medianas empresas. Se otorgan
direcciones de clase C para todos los dems solicitantes. Cada clase de
red permite una cantidad fija de equipos (hosts).
1
El nmero binario de 8 bits ms alto es 11111111 y esos bits, de derecha a izquierda, tienen valores
decimales de 1, 2, 4, 8, 16, 32, 64 y 128, lo que suma 255 en total.
2
Aunque en el pasado se le hayan otorgado a empresas de gran envergadura como, por ejemplo, Hewlett
Packard.

115
Comunicaciones

En una red de clase A, se asigna el primer octeto para identificar la red,


reservando los tres ltimos octetos (24 bits) para que sean asignados a
los hosts, de modo que la cantidad mxima de hosts es 224 menos dos:
las direcciones reservadas de broadcast (tres ltimos octetos a 255) y
de red (tres ltimos octetos a 0), es decir, 16.777.214 equipos.

En una red de clase B, se asignan los dos primeros octetos para


identificar la red, reservando los dos octetos finales (16 bits) para
que sean asignados a los equipos, de modo que la cantidad mxima
de equipos es 216 (de nuevo menos dos), lo que equivale a 65.534
equipos.

En una red de clase C, se asignan los tres primeros octetos para


identificar la red, reservando el octeto final (8 bits) para que sea
asignado a los equipos, de modo que la cantidad mxima de equipos
es 28 (menos dos), o 254 equipos.

La direccin 0.0.0.0 es utilizada por las mquinas cuando estn


arrancando o no se les ha asignado direccin.

La direccin que tiene a cero su parte destinada a equipos sirve para


definir la red en la que se ubica. Se denomina direccin de red.

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.

Las direcciones 127.x.x.x se reservan para pruebas de


retroalimentacin. Se denomina direccin de bucle local o loopback.

Hay ciertas direcciones en cada clase de direccin IP que no estn


asignadas y que se denominan direcciones privadas. Las direcciones
privadas pueden ser utilizadas por los equipos que usan traduccin de
direccin de red (NAT) para conectarse a una red pblica o por los hosts
que no se conectan a Internet. En una misma red no pueden existir dos
direcciones iguales, pero s se pueden repetir en dos redes privadas que
no tengan conexin entre s directamente. Las direcciones privadas son:

Clase A: 10.0.0.0 a 10.255.255.255 (8 bits red, 24 bits equipos)

Clase B: 172.16.0.0 a 172.31.255.255 (16 bits red, 16 bits equipos)

Clase C: 192.168.0.0 a 192.168.255.255 (24 bits red, 8 bits equipos)

Muchas aplicaciones requieren conectividad dentro de una sola red,


y no necesitan conectividad externa. En las redes de gran tamao a
menudo se usa TCP/IP. Por ejemplo, los bancos pueden utilizar TCP/
IP para conectar los cajeros automticos que no se conectan a la red

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.

Las direcciones privadas se pueden utilizar junto con un servidor de


traduccin de direcciones de red (NAT) para suministrar conectividad a
todos los equipos de una red que tiene relativamente pocas direcciones
pblicas disponibles. Segn lo acordado, cualquier trfico que posea una
direccin destino dentro de uno de los intervalos de direcciones privadas
no se enrutar a travs de Internet.

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.

Los puertos se representan como nmeros en rango de 16 bits, que


pueden ir desde el 0 hasta el 65535. Los puertos por debajo del 1024
se denominan puertos privilegiados y en sistemas como los UNIX se
requieren permisos de super-usuario (o root) para poder emplear estos
3
puertos .

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

Cada protocolo de transporte puede hacer uso del rango de


numeracin sin colisionar con ningn otro. TCP puede hacer uso
del puerto 22 e igualmente UDP podra usar el mismo sin provocar
colisin. La asignacin de puertos es realizada por los protocolos
de transporte, cada uno mantiene su propia numeracin.

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

El puerto activo toma de la numeracin de puertos un nmero y lo reserva


para establecer la comunicacin con el puerto pasivo. El nmero del
puerto activo puede ser elegido o no dependiendo del protocolo de
transporte.

La comunicacin se identifica para el sistema operativo con el par de


puertos activo/pasivo adems de la direccin IP tanto de origen como de
destino. Esto hace posible que a un puerto pasivo se pueda conectar ms
de un puerto activo.

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.

En el esquema cliente-servidor existe un servidor que se mantiene a la


espera de una peticin entrante y un cliente de forma activa realizas
las peticiones al servidor. El servidor empleara un puerto pasivo para
establecer la comunicacin mientras que el cliente usara uno activo.

Como hemos ido comentando a lo largo de la seccin hay dos protocolos


para transporte que emplean los puertos de comunicacin:

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.

2. Servidor y Cliente UDP


Erlang provee un mdulo llamado gen_udp que nos permite establecer
un puerto pasivo para mantenernos enlazados y recibir paquetes

118
Comunicaciones

entrantes. Tambin permite emplear un puerto activo para el envo de


un paquete hacia un servidor que se mantenga enlazado. Resumiendo,
permite tanto programar servidores como clientes UDP.

En UDP tanto cliente como servidor deben enlazar un puerto. El servidor


lo har de forma pasiva para recibir mensajes. El cliente lo har de
forma activa para enviarlos y tener la posibilidad de recibir respuesta del
servidor.

El mdulo gen_udp aprovecha las capacidades propias de los procesos


enviando cada paquete recibido al proceso que solicit el enlace con el
puerto.

Hay tres funciones que emplearemos con mucha frecuencia en la


construccin de servidores y clientes UDP: open/1 o open/2, send/4
y close/1.

La funcin open/2 se presenta:

open(Port, Opts) -> {ok, Socket} | {error, Reason}

Puede recibir como segundo parmetro opciones que permiten variar la


forma en la que establecer el enlace con el puerto. Las opciones son:

list | binary
Indica si se quiere recibir el paquete como una lista o un binario. El
valor por defecto es list.

{ip | ifaddr, ip_address()}


La direccin IP en la que enlazar el puerto.

inet | inet6
Emplea IPv4 o IPv6 para las conexiones. El valor por defecto es inet.

{active, true | false | once}


Indica si todos los mensajes recibidos por red sern pasados al
proceso (true) o no (false) o si se har slo la primera vez (once). El
valor por defecto es true.

{reuseaddr, true | false}


Permite reutilizar el puerto. No lo bloquea. Por defecto esta opcin
est deshabilitada.

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

Pondremos en prctica lo aprendido. Vamos a escribir un mdulo que


enlace el puerto 2020 y cada paquete que reciba lo pase por pantalla:

-module(udpsrv).
-export([start/1, init/1, loop/1]).

-record(udp, {socket, ip, port, msg}).

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.

El cdigo se inicia mediante la funcin start/1 indicando un nmero


entero correspondiente al puerto enlazado. Lanza un nuevo proceso que
ejecuta la funcin init/1 encargada de abrir el puerto y pasar el control
a la funcin loop/1 que se encarga de atender los paquetes que vayan
llegando.

En el ejemplo hemos usado un registro formado por los cuatro datos que
nos enva cada paquete UDP adems del identificador:

{udp, Socket, IP, InPortNo, Packet}

La definicin de estos datos es la siguiente:

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).

Podemos escribir estas lneas en la consola de Erlang para probar el


cdigo del servidor:

> {ok, Socket} = gen_udp:open(0).


{ok,#Port<0.618>}
21> gen_udp:send(Socket, {127,0,0,1}, 2020, "hola mundo!").
ok
recibido({127,0,0,1}): "hola mundo!"

La operacin de conexin se ha realizado abriendo una comunicacin


con la funcin gen_udp:open/1 pasando como parmetro el puerto 0.
El puerto 0 se usa para indicar que queremos que el sistema operativo
seleccione un puerto automticamente por nosotros. Podemos recurrir
a ejecutar de nuevo inet:i/0 para ver las conexiones abiertas y las
estadsticas:

> inet:i().
Port [...] Recv Sent Owner Local Address [...] State Type
593 [...] 11 0 <0.34.0> *:2020 [...] BOUND DGRAM

121
Comunicaciones

618 [...] 0 11 <0.38.0> *:33361 [...] BOUND DGRAM


ok

Ahora vemos dos lneas. La primera sigue siendo la del servidor y la


segunda pertenece al cliente. Por los datos estadsticos vemos que la
informacin que ha enviado el cliente (columna Sent) es la que ha
recibido el servidor (columna Recv).

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]).

-record(udp, {socket, ip, port, msg}).

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.

Hemos empleado la funcin gen_udp:send/4 para enviar al remitente


una respuesta enviando un texto fijo al cliente. Podemos probarlo en
consola de la siguiente forma:

> 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"}}

El cliente recibe un paquete del servidor en el que le dice recibido. En la


funcin gen_udp:open/2 hemos empleado el parmetro de opciones
para indicar a gen_udp que no enve el paquete recibido al proceso. Al

122
Comunicaciones

ejecutar gen_udp:recv/2 es cuando se obtiene la informacin que


llega del servidor.

3. Servidor y Cliente TCP


Para establecer comunicaciones TCP en Erlang disponemos del mdulo
gen_tcp. En TCP el servidor escucha de un puerto y se mantiene
aceptando peticiones entrantes que quedan conectadas tras su
aceptacin. La dinmica est protocolizada de forma que un servidor
establece una escucha en un puerto especfico con posibilidad de envo
de opciones a travs de la funcin listen/2 cuya definicin es:

listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}

Las opciones disponibles son iguales a las que se mostraron en la funcin


gen_udp:open. Son las siguientes:

list | binary
Indica si se quiere recibir el paquete como una lista o un binario. El
valor por defecto es list.

{ip | ifaddr, ip_address()}


La direccin IP en la que enlazar el puerto.

inet | inet6
Emplea IPv4 o IPv6 para las conexiones. El valor por defecto es inet.

{active, true | false | once}


Indica si todos los mensajes recibidos por red sern pasados al
proceso (true) o no (false) o slo la primera vez (once). El valor por
defecto es true.

{reuseaddr, true | false}


Permite reutilizar el puerto. No lo bloquea. Por defecto esta opcin
est deshabilitada.

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

Si ponemos en escucha el puerto 2020 y volvemos a listar el estado de


la red podemos ver que el proceso (en Owner) pasa a escuhar (State =:=
LISTEN) en el puerto 2020 (en Local) y para el tipo TCP (Type =:= STREAM):

> {ok, Socket} = gen_tcp:listen(2020, [{reuseaddr, true}]).


{ok,#Port<0.609>}
3> inet:i().
Port [...] Recv Sent Owner Local Address [...] State Type
609 [...] 0 0 <0.32.0> *:2020 [...] LISTEN STREAM
ok

El siguiente paso ser mantener el proceso a la espera de una conexin


entrante. Es el proceso que se llama aceptacin y se realiza con la
funcin accept/1. Si lo ejecutamos en la consola de Erlang el sistema
se quedar bloqueado a la espera de una conexin entrante:

> {ok, SockAceptado} = gen_tcp:accept(Socket).

La funcin emplea la variable Socket creada en listen/2 para esperar


nuevas conexiones entrantes. Cuando una conexin entrante llega la
funcin accept/1 acepta la conexin y finaliza su ejecucin retornando
otra variable SockAceptado. Este socket se emplear para comunicarse
con el cliente y obtener informacin del mismo.

En otra consola podemos realizar la conexin entrante. Emplearemos la


funcin connect/3 que tiene la siguiente sintaxis:

connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}

Como parmetros se indica la direccin IP a la que conectarse (Address


en formato tupla), el puerto al que conectarse (Port) y las opciones para
establecer la comunicacin (Options). Las opciones son las mismas que
se describieron para la funcin listen/2.

Abrimos otra consola y establecemos una comunicacin local entre


ambos puntos escribiendo lo siguiente:

> {ok, Socket} = gen_tcp:connect({127,0,0,1}, 2020, []).


{ok,#Port<0.599>}
2> inet:i().
Port [...] Recv Sent [...] Local Foreign State Type
599 [...] 0 0 [...] *:38139 *:2020 CONNECTED STREAM
ok

La nueva consola de Erlang slo tiene constancia de una conexin


existente que est en estado conectada (State) y va del puerto local
38139 al puerto 2020.

Desde la nueva consola podemos enviar informacin al servidor a travs


de la funcin send/2 la cual tiene la forma:

124
Comunicaciones

send(Socket, Packet) -> ok | {error, Reason}

Como Socket emplearemos el que nos retorn la funcin connect/2.


El Packet es la informacin que queremos enviar. El paquete permite
formatos de lista de caracteres y lista binaria. Podemos ejecutarlo de la
siguiente manera:

> gen_tcp:send(Socket, "hola mundo!").

En el servidor no vemos de momento nada por consola. Si ejecutamos


5
flush/0 aparecern los mensajes recibidos al proceso de la consola .

Si agregamos a las opciones de la funcin listen/2 {active, false} el


mensaje no es enviado al proceso sino que espera a que lo recibamos
a travs del uso de la funcin recv/2. Esta funcin tiene la siguiente
sintaxis:

recv(Socket, Length) -> {ok, Packet} | {error, Reason}

El Socket es el valor de retorno obtenido tras la ejecucin de la funcin


accept/1. Length es el tamao mximo que se espera recibir del
paquete. En caso de que el paquete sea mayor que el tamao una nueva
ejecucin de recv/2 recoger el siguiente trozo de informacin. En caso
de indicar un tamao cero se recibe todo el paquete sin limitacin de
tamao.

Para establecer el dilogo entre servidor y cliente slo necesitamos


emplear las funciones send/2 y recv/2 para realizar la comunicacin
bidireccional. En el momento en el que se desee finalizar la
comunicacin por cualquiera de las dos partes emplearamos la funcin
close/1.

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.

4. Servidor TCP Concurrente


La comunicacin TCP se basa en la interconexin de dos puntos. En
Erlang se conecta cada socket a un proceso para recibir informacin por
5
Recordemos que la consola es un proceso que se mantiene a la espera de recibir eventos. Todos los
eventos que reciba la consola son interceptados por esta. Vase apndice B para ms informacin.

125
Comunicaciones

lo que hasta que la conexin entre cliente y servidor no se cierra ningn


otro cliente puede ser atendido por el servidor. Este problema no se
presenta en comunicaciones UDP. En TCP cada conexin servidora genera
un socket de conexin con el cliente especfico.

Para que el proceso de servidor no permanezca bloqueado atendiendo


la conexin del primer cliente que conecte generamos un nuevo proceso
para atender esa peticin entrante. De esta forma el proceso principal
queda liberado para aceptar ms peticiones y generar nuevos procesos
a medida que vayan llegando nuevas peticiones de clientes.

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:

controlling_process(Socket, Pid) -> ok | {error, Reason}

Escribiremos un pequeo mdulo para comprobar cmo funciona.


Llamaremos al mdulo tcpsrv y agregaremos las funciones del servidor:

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.

La funcin start/1 se encarga de lanzar en un proceso aparte la


ejecucin de la funcin srv_init/1. El cometido de esta funcin
inicializadora es establecer la escucha en un puerto TCP. El bucle de
ejecucin para el servidor se basa en aceptar una conexin, generar un

126
Comunicaciones

nuevo proceso, pasarle el control de la conexin con el cliente al nuevo


proceso y vuelta a empezar.

La generacin del nuevo proceso tiene como funcin de bucle principal a


worker_loop. Esta funcin integrara el protocolo a nivel de aplicacin
para interactuar con el cliente. En nuestro ejemplo esperamos a recibir
un mensaje y lo retornamos precedido de la palabra Eco.

El siguiente cdigo es un ejemplo para probar el servidor:

cli_send(Port, Msg) ->


Opts = [{active, true}],
{ok, Socket} = gen_tcp:connect({127,0,0,1}, Port, Opts),
gen_tcp:send(Socket, Msg),
receive
{tcp, Socket, MsgSrv} ->
io:format("Retornado ~p: ~p~n", [self(), MsgSrv]);
Any ->
io:format("Mensaje no reconocido: ~p~n", [Any])
end,
gen_tcp:close(Socket).

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.

Hasta el momento todo funciona como la versin anterior. No obstante,


hemos agregado en el servidor un retraso de 5 segundos que nos ayudar
a ver la concurrencia en ejecuciones mltiples de cli_send/2. Lo
podemos realizar con varias consolas o a travs de un cdigo como el
siguiente:

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)).

Este cdigo genera 10 procesos que ejecutan la funcin cli_send/2


enviando el mensaje i=I, siendo I el valor pasado por foreach/2 a cada
uno de los procesos.

La ejecucin muestra como todos los procesos llegan a recibir el mensaje


en el servidor y quedan esperando por el resultado 5 segundos despus.

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

la capacidad de interpretar los paquetes recibidos a travs de TCP o UDP


y enviarlos como mensaje al proceso ya procesados.
7
Segn la documentacin de inet los formatos que procesa son: CORBA,
ASN-1, SunRPC, FastCGI, Line, TPKT y HTTP.

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.

Para construir nuestro propio servidor HTTP y aprovechar la caracterstica


que nos provee inet slo tendramos que agregar la opcin a la funcin
listen/2. Vamos a verlo con un ejemplo:

> Opts = [{reuseaddr, true}, {active, true}, {packet, http}],


> {ok, Socket} = gen_tcp:listen(8080, Opts).
{ok,#Port<0.604>}
> {ok, SC} = gen_tcp:accept(Socket).

En este momento el sistema queda en espera de que llegue una peticin.


Como hemos levantado un puerto TCP y le hemos configurado las
caractersticas de HTTP, vamos a abrir un navegador con la siguiente URL:

http://localhost:8080/

En la consola veremos que ya prosigue la ejecucin:

{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_request, 'GET', {abs_path,"/"},{1,1}}

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.

A continuacin vemos un ejemplo completo. Presenta las peticiones


recibidas por pantalla junto con su contenido:

-module(httpsrv).
-export([start/1]).

-define(RESP, "HTTP/1.1 200 OK


Content-Length: 2
Content-Type: text/plain

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

io:format("Recibido ~p: ~p~n", [self(), Msg]),


gen_tcp:send(Socket, ?RESP),
gen_tcp:close(Socket);
{tcp_closed, Socket} ->
io:format("Finalizado.~n"),
gen_tcp:close(Socket);
Any ->
io:format("Mensaje no reconocido: ~p~n", [Any]),
gen_tcp:close(Socket)
end.

El servidor es bastante simple ya que siempre retorna el mismo resultado.


Si accedemos desde un navegador veremos en modo texto el mensaje
OK.

En la funcin worker_loop/1 cuando se recibe http_eoh se puede


ver que se modifica el tipo de paquete para poder recibir el contenido.
Adems vemos que se diferencian bien los mensajes que se reciben de
tipo http de los que son de tipo tcp.

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

Un ecosistema es un ambiente en el que conviven elementos en


un espacio relacionandose entre s. En software se ha tomado esta
definicin para definir al conjunto de herramientas y sistemas que
permiten realizar software.

En Erlang lo usaremos para identificar el uso de unas herramientas junto


con sus buenas prcticas a la hora de desarrollar proyectos de software.

Para este fin daremos un repaso a la herramienta de construccin rebar


que ha llegado a convertirse en un estndar dentro de la comunidad de
Erlang.

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.

La herramienta rebar es la ms empleada entre las utilidades de terceros


1
del mundo Erlang. La empresa Basho es la desarrolladora principal de
esta herramienta aunque cada da hay ms contribuidores al proyecto.

Un proyecto en Erlang/OTP debe disponer de una estructura base como


la siguiente:

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.

Crearemos un proyecto que ilustre cmo organizar los ficheros del


cdigo y cmo ejecutar ese mismo cdigo de forma autnoma.

Para esta tarea nos ayudaremos de rebar. Creamos los tres directorios
base y pasamos a instalar rebar.

1.1. Instalar rebar


La instalacin de rebar se basa en descargar el cdigo de su repositorio y
ejecutar el script bootstrap. El sistema generar el script rebar en ese
mismo directorio. Este script se puede copiar a un directorio del PATH o
localmente dentro del proyecto que estemos desarrollando.

$ git clone git://github.com/basho/rebar.git


$ cd rebar
$ ./bootstrap
Recompile: src/getopt
...
Recompile: src/rebar_utils
==> rebar (compile)
Congratulations! You now have a self-contained script called
"rebar" in your current working directory. Place this script
anywhere in your path and you can use rebar to build OTP-
compliant apps.

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

Para asegurarnos de que es accesible podemos ejecutarlo en la consola.

Nota
La utilidad rebar se encuentra tambin disponible para Windows
3
a travs del repositorio bifurcado (fork) de IRONkyle .

Recomiendo que los proyectos iniciales y el aprendizaje se lleven


a cabo en sistemas tipo Unix como MacOS o GNU/Linux por el
motivo de que la mayora de soporte y desarrollos se realizan en
estos sistemas.

1.2. Escribiendo el Cdigo


Vamos a crear un proyecto que consistir en un servidor web al que se le
solicitarn ficheros y en caso de existir en el directorio priv se retornar
su contenido.

El desarrollo lo realizaremos en dos ficheros. El primer mdulo se


encargar de establecer la escucha para el servidor web y atender las
peticiones:

-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).

worker_loop(Socket, State) ->


receive
{http, Socket, {http_request, Method, TPath, _}} ->
{abs_path, Path} = TPath,
error_logger:info_msg("Peticion: ~p~n", [Path]),
worker_loop(Socket, State ++ [
{method, Method}, {path, Path}
]);
{http, Socket, {http_header, _, Key, _, Value}} ->
worker_loop(Socket, State ++ [{Key, Value}]);
{http, Socket, http_eoh} ->
Response = fileserver:send(State),
gen_tcp:send(Socket, Response),
gen_tcp:close(Socket);

3
https://github.com/IRONkyle/rebar

133
Ecosistema Erlang

{tcp_closed, Socket} ->


error_logger:info_msg("Finalizado.~n"),
gen_tcp:close(Socket);
Any ->
error_logger:info_msg("No reconocido: ~p~n", [Any]),
gen_tcp:close(Socket)
end.

El cdigo listado fue visto en la Seccin 4, Servidor TCP Concurrente


del Captulo7, Comunicaciones. Solo agregaremos la llamada al mdulo
fileserver.

El segundo mdulo se encargar de buscar el fichero solicitado y


retornarlo como texto identificando su tipo. El cdigo es el siguiente:

-module(fileserver).
-export([send/1]).

-define(RESP_404, <<"HTTP/1.1 404 Not Found


Server: Erlang Web Server
Connection: Close

">>).

-define(RESP_200, <<"HTTP/1.1 200 OK


Server: Erlang Web Server
Connection: Close
Content-type: ">>).

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.

Con esto ya tenemos el cdigo preparado. Solos nos falta escribir la


definicin necesaria para que rebar pueda identificar la aplicacin y
construir el producto final. Crearemos el fichero en el directorio src con
el nombre webserver.app.src:

{application, webserver , [
{description , "Erlang Web Server"},
{vsn , "1.0"},
{applications ,[
kernel, stdlib, inets
]}
]}.

El nombre que tendr la aplicacin.


Como descripcin se especifica un texto. Es deseable que no sea
muy extenso. Se puede poner el nombre completo de la aplicacin.
La versin de la aplicacin. Se puede especificar de la forma que
se desee.
Aplicaciones que deben de iniciarse antes de iniciar la nuestra.
Dependencias.

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

El espacio de trabajo es como se puede observar a continuacin:

El directorio ebin contiene la compilacin de los cdigos listados en


la seccin anterior. El fichero webserver.app.src se analiza y se
completa para generar el fichero webserver.app dentro del directorio
ebin.

Si queremos ejecutar el cdigo, podemos iniciar la consola de la


siguiente forma:

$ erl -sname webserver -pa ebin


(webserver@bosqueviejo)1> webserver:start(8888).
<0.39.0>

De esta forma tenemos el cdigo en ejecucin. Para eliminar estos


ficheros generados ejecutamos el comando rebar clean.

3. Creando y lanzando una aplicacin


En la seccin anterior vimos que el lanzamiento del cdigo escrito se
haca de forma manual. Si desarrollamos una aplicacin de servidor hay
que poder lanzar esta aplicacin de forma automtica.

Erlang proporciona al programador una forma de realizar esto a travs de


5
un comportamiento denominado application.

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).

-export([start/0, start/2, stop/1]).

start() ->
application:start(webserver).

start(_StartType, _StartArgs) ->


{ok, webserver:start(8888)}.

stop(_State) ->
ok.

Este mdulo dispone de tres funciones. Las funciones start/2 y


stop/1 son requeridas por el comportamiento application, mientras que
start/0 la emplearemos para la lnea de comandos.

En el fichero webserver.app.src solo debemos de agregar una nueva


lnea que indique qu mdulo se har cargo de las llamadas propias de
la aplicacin para su inicio y fin:

{application, webserver, [
{description, "Erlang Web Server"},
{vsn, "1.0"},
{applications,[
kernel, stdlib, inets
]},
{mod, {webserver_app, []}}
]}.

Lnea que indica el mdulo de comportamiento application que se


har cargo del inicio y parada de la aplicacin.
6
En la consola agregaremos un par de argumentos ms :

$ erl -sname test -pa ebin -s inets -s webserver_app \


-noshell -detached

El comando se encarga de dar un nombre al nodo (-sname), decir donde


se encuentra el cdigo que queremos lanzar (-pa), arrancar la aplicacin
inets (-s) y la aplicacin webserver. Indicamos adems que no queremos
que se ejecute una consola o shell (-noshell) y que se ejecute en segundo
plano (-detached).

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

podemos encontrar libreras para conectar con MySQL, PostgreSQL,


Memcached, o frameworks web como ChicagoBoss o Nitrogen, o
frameworks para crear servidores web como cowboy o mochiweb entre
otras muchas.

La herramienta rebar posibilita a travs de su fichero de configuracin


que podamos instalar en nuestro proyecto una librera externa con muy
poco esfuerzo.

El cdigo del fichero fileserver.erl muestra una funcin llamada


mimetype/1. Esa funcin es insuficiente para cubrir todos los tipos
posibles de ficheros que pudisemos utilizar en nuestra aplicacin.
9
Podemos emplear en su lugar la librera mimetypes .

Para ello generaramos el fichero de configuracin rebar.config en la


ruta raz del proyecto y con el siguiente contenido:

{deps, [
{mimetypes, ".*",
{git, "https://github.com/spawngrid/mimetypes.git",
"master"}
}
]}.

La especificacin de la aplicacin la cambiamos tambin para agregar la


nueva dependencia de la siguiente manera:

{application, webserver, [
{description, "Erlang Web Server"},
{vsn, "1.0"},
{applications,[
kernel, stdlib, inets, mimetypes
]},
{mod, {webserver_app, []}}
]}.

Agregamos en el fichero webserver_app.erl el lanzamiento de la


aplicacin mimetypes para cumplir con la dependencia. La funcin
start/0 quedara as:

-module(webserver_app).
-behaviour(application).

-export([start/0, start/2, stop/1]).

start() ->
application:start(mimetypes),
application:start(webserver).

start(_StartType, _StartArgs) ->


{ok, webserver:start(8888)}.

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]).

-define(RESP_404, <<"HTTP/1.1 404 Not Found


Server: Erlang Web Server
Connection: Close

">>).

-define(RESP_200, <<"HTTP/1.1 200 OK


Server: Erlang Web Server
Connection: Close
Content-type: ">>).

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.

Antes de compilar debemos de lanzar el siguiente comando para


descargar las dependencias que hemos indicado que necesitamos en
nuestro proyecto:

$ 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)

Para lanzar de nuevo la aplicacin agregaremos la ruta de las


dependencias de esta forma:

$ erl -pa deps/*/ebin -pa ebin -sname test -s inets \


-s webserver_app -noshell -detached

Vemos al ejecutarlo que volvemos a tener el puerto 8888 disponible y


los ficheros solicitados presentan ya unos tipos MIME ms precisos:

$ netstat -tln | grep 8888


tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN
$ curl -i http://localhost:8888/rebar.config
HTTP/1.1 200 OK
Server: Erlang Web Server
Connection: Close
Content-type: application/octet-stream
Content-lenght: 119

{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

La herramienta rebar nos facilita la tarea de la liberacin generando una


serie de scripts y ficheros de configuracin. Crearemos el directorio rel y
dentro de l ejecutaremos el comando:

$ rebar create-node nodeid=webserver

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.

Lo siguiente ser crear el directorio apps y un subdirectorio webserver.


Dentro de webserver moveremos los directorios src y ebin.

Ajustaremos los ficheros rebar.config y el nuevo fichero dentro del


directorio rel llamado reltool.config para adaptarlos a la nueva
ubicacin del cdigo. En el fichero rebar.config basta con agregar
esta lnea:

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.

El fichero reltool.config es la configuracin que necesita el sistema


denominado reltool para generar la liberacin. Las posibles entradas de
configuracin que se pueden agregar al fichero son muy numerosas. Para
profundizar ms el tema puedes visitar su pgina de documentacin
10
oficial . En este apartado recogeremos las ms importantes que se
agregarn bajo la clave sys:

{lib_dirs, [Dir1,Dir2..DirN]}
El directorio (o directorios) que contiene las aplicaciones.
Deberemos de agregarlo de la siguiente forma:

{lib_dirs, ["../apps", "../deps"]},

{rel, App, Vsn, [App1,App2..AppN]}


Se especifica el nombre de la aplicacin (primer parmetro), la
versin de la aplicacin (segundo parmetro) y las aplicaciones a
ejecutar. El listado de aplicaciones debe contener las applicaciones
en el orden en el que se deben de ir iniciando cuando se arranque
el programa. Por lo tanto deberemos de agregar:

{rel, "webserver", "1.0", [


kernel, stdlib, sasl, inets, mimetypes, webserver
]},

{boot_rel, App}
Se pueden crear tantos apartados rel como se necesiten. Uno de
ellos debe marcarse por defecto con esta opcin.

{profile, development | standalone | embedded}


El perfil indica el nivel de restriccin a aplicar para la copia de
dependencias, libreras o binarios entre otros. Hay tres perfiles de
menos a ms restrictivo: development, standalone y embedded.

10
http://www.erlang.org/doc/man/reltool.html

142
Ecosistema Erlang

{incl_cond, include | exclude | derived }


Indica el modo en que sern elegidas las aplicaciones que entrarn
en la liberacin. Las opciones disponibles son:

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.

{mod_cond, all | app | ebin | derived | none}


Es como incl_cond pero a nivel de aplicacin. Las opciones que
permite son:

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.

{app, App, [ConfList]}


Esta es la especificacin individual de cada aplicacin. Debe de
existir al menos la principal. Como configuracin se puede indicar
11
Se refiere a todos los mdulos que no estn includos pero que reltool detecte que puedan ser
utilizados.

143
Ecosistema Erlang

una entrada incl_cond que aplique solo sobre la aplicacin y


opcionalmente otra mod_cond que acte solo sobre la aplicacin.
En nuestro fichero pondremos nicamente:

{app, webserver, [{mod_cond, app}, {incl_cond, include}]}

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.

Otras entradas al mismo nivel de sys que se pueden encontrar son


target_dir que indica el nombre del directorio donde se situar el
resultado de la liberacin y overlay que contiene comandos adicionales
a ejecutar durante la liberacin. Estos comandos adicionales son del
tipo crear directorio (mkdir), copiar fichero (copy) o emplear una plantilla
(template) a fusionar con un fichero de variables y generar el fichero que
estar disponible en el despliegue.

Aqu el fichero completo reltool.config:

{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"}
]}.

Dentro del directorio rel ejecutamos el comando:

$ rebar generate
==> rel (generate)

Obtenemos como resultado un directorio webserver en el que se


encuentra la liberacin. El comando rebar nos proporciona en el
directorio bin un script que nos permite lanzar el programa de varias
formas:

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.

Si ejecutamos el comando start y despus ping podremos ver que el


sistema responde sin problemas. Entramos a consola a travs de attach
y podemos ver los mensajes de log que hayamos escrito en el fichero:

$ webserver/bin/webserver start

145
Ecosistema Erlang

/tmp/rel$ webserver/bin/webserver ping


pong
/tmp/rel$ webserver/bin/webserver attach
Attaching to /tmp/rel/webserver/erlang.pipe.1 (^D to exit)

(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.

El despliegue en un servidor u otro equipo informtico se realizar


comprimiendo el resultado que se ha obtenido en webserver y
descomprimirlo en el destino. El lanzamiento lo podemos agregar como
script en los sistemas tipo Unix gracias a que respeta la forma de start
y stop.

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.

Como ejemplo pensemos que necesitamos modificar el fichero


fileserver.erl para que responda a una solicitud con una URI /help
que retorne un texto personalizado de ayuda.

Lo primero que haremos ser generar la versin 1.0 y modificar el nombre


dentro del directorio rel de webserver a webserver_old.

Hacemos los cambios oportunos en el fichero:

-module(fileserver).
-export([send/1]).

-define(RESP_404, <<"HTTP/1.1 404 Not Found

146
Ecosistema Erlang

Server: Erlang Web Server


Connection: Close

">>).

-define(RESP_200, <<"HTTP/1.1 200 OK


Server: Erlang Web Server
Connection: Close
Content-type: ">>).

-define(HELP_TEXT, <<"Texto de ayuda!">>).

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.

Tambin modificamos la versin dentro del fichero


webserver.app.src para que refleje el cambio de versin y la versin
en el fichero reltool.cfg para que sea 2.0 en lugar de 1.0.

147
Ecosistema Erlang

Volvemos a generar el proyecto compilando y generando el producto


final:

$ rebar clean compile


$ cd rel
$ rebar generate

Tenemos dos directorios de nuestro proyecto. Uno con la versin 1.0 y


otro con la versin 2.0. Es ahora cuando generamos los ficheros appup.
Estos ficheros se generan por aplicacin y contienen informacin sobre
los cambios que hay que realizar en caliente.

Dejamos que rebar generate-appups nos genere todos los ficheros


necesarios:

$ rebar generate-appups previous_release=webserver_old


==> rel (generate-appups)
Generated appup for webserver
Appup generation complete

El fichero generado para nuestra aplicacin es webserver.appup. Este


fichero se crea en la ruta webserver/lib/webserver-2.0/ebin. Su forma es:

{"2.0", [
{"1.0", [
{load_module,fileserver}
]}
], [
{"1.0", [
{load_module,fileserver}
]}
]}.

La versin que va a ser instalada.


Bloque que indica las acciones a llevar para pasar de la versin 1.0
a la 2.0.
Cada opcin a llevar a cabo tendr forma de tupla. La accin
load_module se refiere a la recarga del mdulo que se indica (en
este caso fileserver).
Bloque de las acciones a llevar a cabo en caso de querer realizar una
marcha atrs de la versin 2.0 a la versin 1.0.

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}

Esta forma se aplicara cuando cambiamos fileserver.erl


por otros mdulos diferentes. Primero cargamos los nuevos,
recargamos el manejador, enviamos el cdigo de cambio para
13
el manejador y eliminamos el mdulo antiguo. El soporte
para system_code_change/4 debe de existir en el mdulo
webserver.

Ahora generamos el paquete de la nueva versin. Para ello utilizamos el


comando rebar generate-upgrade de la siguiente forma:

$ rebar generate-upgrade previous_release=webserver_old


==> rel (generate-upgrade)
webserver_2.0 upgrade package created

El resultado ser un fichero webserver_2.0.tar.gz. Este fichero


contiene los binarios del programa para ejecutarse en produccin, as
como la informacin de cada aplicacin y los ficheros para recargar cada
una de manera adecuada.

El fichero debe de copiarse en la ruta webserver/releases. Si hemos


lanzado el cdigo antiguo que est en webserver_old, copiamos dentro
de su directorio releases este fichero. Ejecutamos:

$ 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.

Si accedemos mediante navegador web a la URI /help veremos que el


cdigo se ha actualizado y muestra ahora el nuevo texto de ayuda que
hemos agregado.

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.

Erlang permite la realizacin de este tipo de guiones a travs de escript.


El comando escript interpreta de forma rpida cdigos Erlang que deben
constar de una funcin main/1. Por ejemplo:

#!/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).

La funcin main/1 siempre recibe una lista de cadenas de texto. Si no


hay argumentos la lista est vaca.

Nota
La primera lnea es conocida como shebang o hashbang. Indica el
comando con el que hay que ejecutar ese guin.

150
Ecosistema Erlang

En el cdigo no existe declaracin de mdulo ni exportacin de


funciones. An sin estar compilado puede hacer uso de cualquier librera
de Erlang adems de todos los ejemplos de cdigo vistos anteriormente.

Podemos probarlo as:

$ 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.

Si creamos una estructura de aplicacin normal con su directorio src con


el fichero fact.erl que empleamos cambindolo de esta forma:

-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).

Agregamos tambin el fichero de aplicacin siguiente:

{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

Podemos crear nuestro guin compilado en forma de binario:

$ rebar compile escriptize


==> fact_scriptize (compile)
Compiled src/fact.erl
==> fact_scriptize (escriptize)

Si realizamos la prueba que hicimos con el guin anterior veremos


que se comporta exactamente igual. Esta forma tiene la ventaja de que
se pueden incrustar ms mdulos, dependencias y que el cdigo se
encuentra ya compilado por lo que es ms rpido que en el ejemplo
anterior.

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.

Lo aprendido a lo largo de estas pginas constituye una base de


conocimiento y una forma de trabajo con el lenguaje, pero ese
conocimiento debe ser ampliado a travs del estudio de OTP para brindar
mejores soluciones al cdigo escrito.

Espero que el libro haya resultado til, ameno y que la curiosidad


despertada por Erlang haya sido satisfecha e incluso las ganas de seguir
aprendiendo con el siguiente volumen de este libro.

Hasta entonces, un saludo y suerte con el cdigo.

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 .

En este apndice veremos como bajar e instalar Erlang, tanto si lo


queremos hacer desde paquetes listos para funcionar directamente o
desde cdigo fuente.

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.

La instalacin en estos sistemas se divide en varios pasos. Se seleccionan


los paquetes a instalar y la ruta:

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.

En un futuro se plantea la eliminacin de estas libreras en favor


de otras de mingw (una versin de GCC para Windows) que nos
permitirn saltar esta parte.

Seguimos con la instalacin hasta que el instalador nos informe de que


ha finalizado con xito. En el men de inicio veremos que se ha creado
un nuevo grupo de programas. Podemos lanzar el que tiene como ttulo
Erlang con el logotipo del lenguaje a su lado para que se muestre la
siguiente pantalla:

Ahora ya tenemos lista la consola de Erlang. Podemos tomar cualquier


ejemplo del libro para probar su funcionamiento.

2. Instalacin en sistemas GNU/Linux


La mayora de distribuciones GNU/Linux disponen de sistemas de
instalacin de paquetes de forma automatizada. Erlang est disponible
por defecto en la mayora de estas distribuciones pero, dado que estos
paquetes en muchas de estas distribuciones se marcaron como estables

155
Instalacin de Erlang

hace mucho tiempo las versiones de Erlang disponibles pueden ser algo
antiguas.

2.1. Desde Paquetes Binarios


Tenemos la opcin de descargar un paquete actualizado e instalarlo en
lugar del que provee por defecto la distribucin que estemos usando.
Los paquetes actuales se pueden descargar desde la web de Erlang
Solutions:

https://www.erlang-solutions.com/downloads/download-erlang-otp

Las versiones para CentOS y Fedora se descargan en formato RPM y


pueden instalarse a travs de la herramienta rpm.

Las versiones para Debian, Ubuntu y Raspbian se descargan en formato


DEB y pueden instalarse a travs de la herramienta dpkg.

Una vez instalado podemos ejecutar desde consola el comando erl o erlc
entre otros.

2.2. Compilando el Cdigo Fuente


Otra opcin es compilar el cdigo fuente para los sistemas en los que
no se encuentre Erlang en la ltima versin o se quiera disponer de
varias versiones instaladas en rutas diferentes a las que se establecen
por defecto.

En este caso habr que descargar el ltimo archivo comprimido de


cdigo:

# 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

La compilacin requiere que se disponga en el sistema de un compilador


y las libreras en las que se basa Erlang. El comando configure nos dar
pistas sobre las libreras que haya que instalar.

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 .

En sistemas como OpenSolaris o BSD (FreeBSD, OpenBSD o NetBSD)


la solucin ms comn es instalar desde cdigo fuente tal y como se
coment en la seccin Seccin2.2, Compilando el Cdigo Fuente .

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.

La lnea de comandos es por tanto uno de los principales elementos


de Erlang. En este apndice veremos las opciones que nos ofrece este
intrprete de comandos para facilitar la tarea de gestin. Muchas de estas
funciones ya se han ido mostrando a travs de los captulos del libro por
lo que este compendio ser una referencia til para nuestro trabajo del
da a da.

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:

> rd(state, {hits, miss, error}).

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

rr(Modulo) / rr(Wildcard) / rr(MoW,R) / rr(MoW,R,O)


Carga los mdulos de uno o varios ficheros. Los ficheros se pueden
indicar mediante el nombre de un mdulo (vase m()) o el nombre
de un fichero o varios con el uso de comodines (wildcard). Se
puede agregar un segundo parmetro que indique el registro que
se desea cargar y un tercer parmetro que se usar como conjunto
1
de opciones .

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.

Adems de los comandos, la consola de Erlang tambin almacena los


ltimos resultados. El nmero de resultados almacenados tambin es
configurable.

Estas son las funciones que pueden emplearse:

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.

Estos son los comandos que podemos emplear en este modo:

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.

La podemos lanzar de la siguiente manera:

> toolbar:start().

Se abrir una ventana como la siguiente:

Los cuatro botones que se pueden observar en la imagen son (de


izquierda a derecha):

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.

El cdigo fuente de estas aplicaciones est disponible junto con


el cdigo fuente de Erlang, en el directorio:

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.

La aplicacin al lanzarse genera un proceso principal que puede estar


enlazado con otros. Esta relacin de procesos es la que se monitoriza
bajo el nombre propio de cada aplicacin que se ejecuta.

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.

Estas aplicaciones son botones que si se presionan nos muestran


una jerarqua de procesos. Cada proceso se identifica con su nombre
registrado o con su PID en caso de no disponer de nombre. Sobre cada
proceso podemos ejecutar una serie de acciones que se representan con
los botones superiores que se pueden ver en la ventana:

165
Herramientas grficas

La barra superior de botones es como una barra de herramientas.


Siempre hay un botn que aparece como marcado indicando as la
opcin que se har sobre cualquier proceso cuando se haga clic sobre
l. Las acciones son:

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.

El men de la ventana principal dispone adems de otras opciones en


su men Actions como: Reboot, Restart, Stop y Ping. Estas opciones son
tiles cuando se emplea la herramienta con otros nodos, ya que permite
reiniciar estos nodos y cuando se vean como desconectados, hacerles
ping para volver a conectarlos.

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.

Esto es posible a travs del men Nodes, donde se listarn todos


los nodos a los que est conectada la mquina virtual de Erlang.

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.

Esta interfaz permite activar el trazado de procesos en una ventana


aparte, visualizando en ella los mensajes entrantes al proceso que se est
trazando. Para trazar un proceso solo hay que seleccionarlo y a travs del
men Trace seleccionar la opcin Trace selected process.

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.

Esto es posible a travs del men Nodes, donde se listarn todos


los nodos a los que est conectada la mquina virtual de Erlang.

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).

A travs del men View se puede conmutar entre la visualizacin de


las tablas ETS y las tablas Mnesia. En el men Options hay opciones
referentes a la visualizacin de las tablas en el listado: refrescar, ver
tablas del sistema o no legibles son algunas de las opciones.

Si hacemos doble clic sobre cualquier entrada de la tabla principal, se


abrir una segunda ventana en la que se mostrar el contenido de la tabla
ETS seleccionada. Se visualiza como si fuese una hoja de clculo y se
marca sobre las columnas con el smbolo de una llave cul es la clave
primaria de la tabla.

Esta es una ventana de visualizacin y edicin por lo que podemos


seleccionar las entradas de la tabla y editarlas o eliminarlas a travs de
las opciones disponibles en el men Edit.

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.

Esto es posible a travs del men File, opcin Nodes..., donde


se listarn todos los nodos a los que est conectada la mquina
virtual de Erlang.

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.

Podemos lanzar este programa de la siguiente manera:

> observer:start().

La ventana abierta dispone de un conjunto de pestaas o lengetas que


disponen de todas las funcionalidades del conjunto de programas vistos
en las secciones anteriores:

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

La ventana tiene este aspecto:

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.

Esto es posible a travs del men Nodes, donde se listarn todos


los nodos a los que est conectada la mquina virtual de Erlang
adems de dar la posibilidad de conectar con otros nodos que no
se encuentren en el listado.

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.

El depurador se lanza desde consola as:

> debugger:start(local).

La ventana que se abre tendr esta forma:

170
Herramientas grficas

La forma ms sencilla de emplear el depurador es a travs del men


Module, opcin Interpret... cargar un fichero de cdigo fuente. En el
propio men Module debe de agregarse despus una opcin con el
nombre del mdulo cargado.

Tras esto, presionamos los tres cuadros de verificacin visibles bajo el


cuadro de la ventana donde aparece el nombre del mdulo: First Call, On
Break y On Exit. Con esto conseguimos que el depurador se active cuando
se suceda cualquiera de estos tres eventos sobre el mdulo.

En la consola de Erlang ejecutamos una funcin del mdulo. Vemos que


se abrir otra ventana de depuracin con el cdigo en la parte principal,
una botonera en la parte media con los botones: Next, Step, Finish, Where,
Up y Down.

171
Herramientas grficas

La parte inferior de la ventana muestra un listado de las variables del


contexto de la funcin que se est depurando a la derecha. En la parte
izquierda hay un evaluador que permite escribir expresiones simples
para comprobacin de datos.

Durante la ejecucin la ventana principal mostrar la informacin de los


procesos que hay en ejecucin, la llamada que los inici, el nombre del
proceso, el estado del proceso (ejecucin, ocioso u otro) e informacin
sobre la ejecucin del proceso.

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.

No es aconsejable lanzar el depurador en un cluster de ms de dos


nodos, ya que la ejecucin concurrente de un mismo mdulo en
varios nodos al mismo tiempo podra llevar a un funcionamiento
inconsistente.

Tomando ejemplos de los ltimos captulos podemos hacer pruebas de


ejecucin del cdigo, probar opciones de compilacin, ver los procesos,
la evaluacin de expresiones, el contenido de las variables y otros
aspectos que nos ayuden a aprender a utilizar bien esta herramienta.

Para ms informacin sobre la misma:

http://www.erlang.org/doc/apps/debugger/debugger_chapter.html

172
173

También podría gustarte