189
Erlang/OTP Volumen I: Un Mundo Concurrente Manuel Angel Rubio Jiménez

erlang-i

Embed Size (px)

Citation preview

Page 1: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 1/189

Erlang/OTPVolumen I: Un Mundo Concurrente

Manuel Angel Rubio Jiménez

Page 2: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 2/189

Erlang/OTPVolumen I: Un Mundo Concurrente

Manuel Angel Rubio Jiménez

Resumen

El lenguaje de programación Erlang nació sobre el año 1986 en los laboratoriosEricsson de la mano de Joe Armstrong. Es un lenguaje funcional con baseen Prolog, tolerante a fallos, y orientado al trabajo en tiempo real y a laconcurrencia, lo que le proporciona ciertas ventajas en lo que a la declaraciónde algoritmos se refiere.

Como la mayoría de lenguajes funcionales Erlang requiere un análisis delproblema y una forma de diseñar la solución diferente a como se haría en unlenguaje de programación imperativo. Sugiere una mejor y más eficiente formade llevarlo a cabo. Se basa en una sintaxis más matemática que programáticapor lo que tiende más a la resolución de problemas que a la ordenación yejecución de órdenes.

Todo ello hace que Erlang sea un lenguaje muy apropiado para la programaciónde elementos de misión crítica, tanto a nivel de servidor como a nivel deescritorio, e incluso para el desarrollo de sistemas embebidos o incrustados.

En este libro se recoge un compendio de información sobre lo que es el

lenguaje, cómo cubre las necesidades para las que fue creado, cómo sacarleel máximo provecho a su forma de realizar las tareas y a su orientación a laconcurrencia. Es un repaso desde el principio sobre cómo programar de unaforma funcional y concurrente en un entorno distribuido y tolerante a fallos.

 

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

Jiménez1 se encuentra bajo una Licencia Creative Commons Reconocimiento-NoComercial-CompartirIgual 3.0 Unported2.

1 http://erlang.bosqueviejo.net/2 http://creativecommons.org/licenses/by-nc-sa/3.0/

Page 3: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 3/189

iii

Tabla de contenidos

Prólogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiIntroducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix

1. Acerca del autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix2. Acerca de los Revisores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x3. Acerca del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi4. Objetivo del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii5. ¿A quién va dirigido este libro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii6. Estructura de la colección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii7. Nomenclatura usada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv8. Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv9. Más información en la web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvi

1. Lo que debes saber sobre Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11. ¿Qué es Erlang? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12. Características de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23. Historia de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54. Desarrollos con Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

4.1. Sector emp re s ar i a l . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74.2. Software libre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

5. Erlang y la Concurrencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105.1. El caso de Demonware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115.2. Yaws contra Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

2. El lenguaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

1. Tipos de Datos ...... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 51.1. Átomos .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151.2. Números Enteros y Reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161.3. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171.4. Listas ..... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181.5. Tuplas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231.6. Registros ....... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2. Imprimiendo por pantal la . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283. Fechas y Horas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

3. Expresiones, Estructuras y Excepciones ........ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331. Expresiones .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

1.1. Expresiones Aritméticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331.2. Expresiones Lógicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341.3. Precedencia de Operadores ..... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

2. Estructuras de Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362.1. Concordancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362.2. Estructura case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372.3. Estructura if  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392.4. Listas de Comprensión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

3. Excepciones ...... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423.1. Recoger excepciones: catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

Page 4: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 4/189

Erlang/OTP

iv

3.2. Lanzar una excepción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433.3. La estructura try ...catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443.4. Errores de ejecución más comunes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

4. Las funciones y módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481. Organización del código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482. Ámbito de las funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503. Polimorfismo y Concordancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514. Guardas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 525. Clausuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526. Programación Funcional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557. Recursividad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58

7.1. Ordenación por mezcla (mergesort ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 597.2. Ordenación rápida (quicksort ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

8. Funciones Integradas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

5. Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 641. Anatomía de un Proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642. Ventajas e inconvenientes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 653. Lanzando Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 674. Bautizando Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685. Comunicación entre Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686. Procesos Enlazados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727. Monitorización de Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758. Recarga de código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 779. Gestión de Procesos ... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8010. Nodos Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

11. Procesos Remoto s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8112. Procesos Locales o Globales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8313. RPC: Llamada Remota a Proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8414. Diccionario del Proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

6. ETS, DETS y Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861. ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

1.1. Tipos de Tabl a s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871.2. Acceso a las ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871.3. Creación de una ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881.4. Lectura y Escritura en ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901.5. Match: búsquedas avanzadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 911.6. Eliminando tuplas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931.7. ETS a fichero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

2. DETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 942.1. Tipos de Tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 952.2. Crear o abrir una DETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962.3. Manipulación de las DETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 982.4. De ETS a DETS y viceversa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

3. Ficheros .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993.1. Abriendo y Cerrando Ficheros ........... . . . . . . . . . . . . . . . . . . . . . . . . . . . . 993.2. Lectura de Ficheros de Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1013.3. Escritura de Ficheros de Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103

Page 5: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 5/189

Erlang/OTP

v

3.4. Lectura de Ficheros Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1033.5. Escritura de Ficheros Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1053.6. Acceso aleatorio de Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

3.7. Lecturas y Escrituras por Lotes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1064. Gestión de Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107

4.1. Nombre del fichero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1074.2. Copiar, Mover y Eliminar Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1084.3. Permisos, Propietarios y Grupos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109

5. Gestión de Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1105.1. Directorio de Trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115.2. Creación y Eliminación de Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115.3. ¿Es un fichero? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1125.4. Contenido de los Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

7. Comunicaciones ....... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114

1. Conceptos básicos de Redes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1141.1. Direcciones IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1151.2. Puertos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

2. Servidor y Cliente UDP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1183. Servidor y Cliente TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1234. Servidor TCP Concurrente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 55. Ventajas de inet  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

8. Ecosistema Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1311. Iniciar un Proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

1.1. Instalar rebar  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321.2. Escribiendo el Código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 3 3

2. Compilar y Limpiar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1353. Creando y lanzando una aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1364. Dependencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1375. Liberar y Desplegar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1406. Actualizando en Caliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1467. Guiones en Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1508. El camino a OTP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152

Apéndices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153A. Instalación de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154

1. Instalación en Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1542. Instalación en sistemas GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

2.1. Desde Paquetes Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1562.2. Compilando el Código Fuente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

3. Otros sistemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157B. La línea de comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158

1. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1582. Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1593. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1604. Histórico .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1605. Procesos .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1616. Directorio de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1627. Modo JCL .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162

Page 6: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 6/189

Erlang/OTP

vi

8. Salir de la consola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163C. Herramientas gráficas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

1. Barra de herramientas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164

2. Monitor de aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1653. Gestor de procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1674. Visor de tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1685. Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1696. Depurador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

Page 7: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 7/189

vii

PrólogoConocí a Manuel Angel cuando me explicó su idea de escribir un librosobre Erlang en Castellano, no sólo me pareció una idea apasionante,sino un hito imprescindible para llevar este lenguaje de programación adonde se merece entre la comunidad castellano-parlante.

Tras intercambiar algunos emails, enseguida me di cuenta de la similitudde nuestras ideas y objetivos: escribir programas eficientes y escalables.Y aunque no lo conocía personalmente, simplemente con ver su dilatadaexperiencia en un abanico tan amplio de tecnologías, ya intuí que elmaterial que saldría de su cabeza sería de ayuda para todo tipo lectores.

En un mundo donde predomina la programación imperativa, loslenguajes funcionales vuelven a cobrar importancia por su potenciay sencillez. La necesidad de sistemas que sean capaces de gestionarmillones de usuarios concurrentes de manera eficiente, ha provocadoque Erlang sea relevante dos décadas después de su creación.

Mi primera experiencia con Erlang fue como ver Matrix, teniendo encuenta que todos los conocimientos que tenía estaban basados enlenguajes orientados a objetos, el primer instinto fue extrapolarlos aaquel primer reto al que me enfrentaba (iterar sobre una lista). Con elpaso de los días empecé a comprender que el salto que estaba realizandono era como aprender otro lenguaje más (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 proporcionade manera nativa Erlang, te permiten diseñar y desarrollar desde elinicio sistemas robustos, evitando tener que resolver problemas deescalabilidad y operaciones complejas en las siguientes fases de unproyecto (capas de cache complejas, despliegues en producción sininterrupciones, optimización de la máquina virtual, ...).

La introducción al lenguaje propuesta por Manuel Angel, desde la base,pasando por los tipos de datos y expresiones, y terminando con lasfuncionalidades nativas que lo diferencian, ayudarán tanto a lectoresnoveles, como a lectores con experiencia en programación funcional.Cuando alguien me comenta que quiere aprender Erlang suelo decir trescosas:

1. Intenta con todas tus fuerzas olvidar todo lo que sepas deprogramación imperativa;

2. Lee un buen libro, completo, desde la introducción hasta las reseñas;

Page 8: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 8/189

Prólogo

viii

3. Ten siempre una consola a mano para ir poniendo en práctica losconocimientos adquiridos.

Hasta el momento de la publicación de este libro, ese consejo estabamuy condicionado al conocimiento de inglés de la persona que lo recibía;y si sumamos todos los nuevos conceptos al que el lector se enfrenta, elresultado no siempre era el esperado. Gracias a este libro, con un estiloclaro y directo, ejemplos útiles y el reflejo de la experiencia del autor,hará que aprender este lenguaje sea una experiencia productiva, de laque espero nazcan desde simples algoritmos rápidos y eficientes, hastasistemas distribuidos altamente escalables.

—José Luis Gordo Romero

Page 9: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 9/189

ix

IntroducciónSorprendernos por algo es el primer paso de la 

mente hacia el descubrimiento.—Louis Pasteur 

1. Acerca del autor

La programación es un tema que me ha fascinado desde siempre. A partirdel año 2002, a la edad de 22 años, me centré en perfeccionar misconocimientos sobre C++, el paradigma de la orientación a objetos y sus

particularidades de implementación en este lenguaje.El lenguaje C++ me abrió las puertas de la orientación a objetos y esemismo año ya comencé a interesarme por Java. Al año siguiente, en 2003,aprendí SQL, Perl y PHP, comenzando así una aventura que me ha llevadoal aprendizaje de nuevos lenguajes de programación regularmente,siempre con el interés de analizar sus potencias y debilidades. Así escomo experimenté también con lenguajes clásicos como Basic, Pascal,Modula-2 y otro tipo de lenguajes de scripting para la gestión de sistemasinformáticos como Perl o lenguajes de shell.

En los siguientes 8 años, después de haber tratado con lenguajesimperativos, tanto estructurados como orientados a objetos, con susparticularidades 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 estructurascomplejas cliente-servidor, en el que los procesos son concurrentes,distribuidos, robustos y tolerantes a fallos. Por si fuera poco, estasestructuras se crean mediante un código compacto y con una sintaxisclara, elegante y fácilmente comprensible.

En el año 2006, encabecé algunos desarrollos en una oficina de I+Den Córdoba que no resultaron del todo favorables. En aquella época, eldesarrollo mediante lenguajes imperativos, y las estructuras propias deconcurrencia y distribución, hicieron que la creación de soluciones fueseexcesivamente costosa y se terminase desechando.

En 2008 volví a retomar el desarrollo de sistemas del área de voz,principalmente en telefonía. Con el bagaje de la experiencia anteriory 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 cantidadde código, en unos tiempos y con una calidad y robustez que parecería

Page 10: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 10/189

Introducción

x

imposible en otros lenguajes. Igualmente comprobamos la simpleza,efectividad y la capacidad de escalado que brinda el lenguaje y sumáquina virtual.

Con todo lo aprendido y hecho en torno a este lenguaje, me he dadocuenta de que hace falta llenar el hueco que deja el no tener literaturasobre Erlang en nuestro idioma, y de paso tratar los temas desde otropunto de vista.

Puedo lanzarme a esta tarea no sin antes recomendar la literaturaexistente que me ha servido como referencia durante mi propio procesode aprendizaje y paso a enumerar. El libro de Joe Armstrong sobre Erlang,completísimo y centrado en el lenguaje base que he releído decenas deveces. El de Francesco Cesarini , igualmente recomendable, aunque más

orientado al desarrollo de proyectos en Erlang. Incluso otro gran libroque he conocido recientemente del equipo que mantiene Erlware 1, muyorientado al framework OTP y la metodología que propone.

2. Acerca de los Revisores

Este libro ha sido revisado por dos personas, sin las cuales, de seguroque sería mucho más complejo de seguir. En esta sección hacemos unapequeña presentación de ambos.

José Luis Gordo RomeroApasionado de la tecnología y del software libre. Durante micarrera profesional he recorrido distintas áreas tecnológicas, loque me ha permitido afrontar proyectos teniendo una perspectivaglobal. Empecé por la administración y automatización de sistemas,pasando por el desarrollo hasta llegar al diseño de arquitectura (enentornos web).

Trabajar en startups me ha permitido explorar y profundizar endiferentes tecnologías, además de poder colaborar en variosproyectos de software libre (de los cuales disfruto aprendiendo y

aportando todo lo que puedo).

Actualmente estoy centrado en varios proyectos donde Erlang esla base, así que haber podido ayudar en la revisión y escribir elprólogo, ha sido todo un placer.

Juan Sebastián Pérez Herrero

Soy experto en diversas tecnologías, con abultada experiencia enentornos web, plataformas de movilidad, integración y gestión

1 http://erlware.com/

Page 11: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 11/189

Introducción

xi

de proyectos internacionales y entornos open source de lo másdiversos.

He sido compañero de trabajo de Manuel, lo que ha servidopara un enriquecimiento mutuo, tanto en conocimientos como enestrategia. Su amplio espectro de conocimiento aporta muchospatrones y antipatrones, su carácter templado hace que siemprese pueda llegar a acuerdos y sus indicaciones, especialmente enel procesamiento a tiempo real y de sistemas con alto número detransacciones me han sido de gran interés.

Me gusta participar en proyectos estimulantes y la edición deeste libro junto con el aprendizaje de Erlang, era una oportunidadde divertirme haciendo una tarea nueva como es la edición de

literatura técnica en español que no estaba dispuesto a dejar pasar.

He realizado la edición de varios capítulos y ha resultado ser unaexperiencia más que interesante. Ponerse en la piel de un lectory facilitarle las cosas sin bajar demasiado el nivel técnico es unreto. Espero que el resultado, probablemente mejorable, facilite lalectura y comprensión de la obra y por ende su difusión.

El presente libro permite formarse en programación concurrenteen Erlang de una forma entretenida. Ya estoy deseando leer lasegunda parte sobre OTP, ya que intuyo que para proyectos de

cierta envergadura se requieren unas directrices claras y el usode buenos patrones de diseño, sobre todo si se busca la robustezque normalmente requieren proyectos críticos que en muchasocasiones manejan transacciones monetarias.

3. Acerca del libro

Durante el año 2008, estuvimos trabajando en proyectos de fuerteconcurrencia para el tratamiento de llamadas telefónicas. Buscábamosque los tiempos de respuesta, la robustez y la agilidad en los desarrollosfuera la necesaria para este tipo de sistemas. Tras haber empleadodiversas herramientas y técnicas de replicación y compartición en basede datos sin obtener resultados totalmente satisfactorios nos decidimosa introducir Erlang. Con ello pudimos observar las capacidades deeste lenguaje y lo bien que se adaptaba a nuestras necesidades derendimiento. Fue entonces cuando nos dimos cuenta de que había muypoca cantidad de información acerca del lenguaje (aunque poco a pocose vaya subsanando), y mucho menos en castellano.

Es complejo adentrarse en un lenguaje nuevo que nada tiene quever con lenguajes con los que se haya trabajado anteriormente (salvoexcepciones como Lisp, Scheme o Prolog), por lo que me decidí a escribir

Page 12: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 12/189

Introducción

xii

el libro que me hubiese gustado encontrar. Un libro con las palabras justas y los diagramas apropiados para poder entender más rápidamentetodos 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 más asequible para el público de habla hispana. El nivel y densidadde ciertas explicaciones son más bajos cuando se tratan en el idiomanativo, lo que hace que sea más fácil de entender.

4. Objetivo del libroCon este libro pretendo cubrir principalmente los aspectos másimportantes dentro del ámbito de aprendizaje de un nuevo lenguaje de

programación:

• Explicar los aspectos básicos del lenguaje para comenzar a programar.Ya que Erlang no es un lenguaje imperativo, puede ocurrir que susintaxis sea paradójicamente más fácil para el que no sabe programarque para desarrolladores avanzados de lenguajes como C, Java o PHP.

• Conocer las fortalezas y debilidades del lenguaje . Como en el uso decualquier tecnología es importante tener la capacidad de seleccionarun lenguaje o entorno frente a otro dependiendo del trabajo que sevaya a realizar. En este texto analizamos qué es Erlang y en qué se

puede emplear, con lo que se obtendrá una idea clara de posiblescasos de uso cuando tenga que acometer un nuevo desarrollo.

Hay muchos casos en los que una mala elección tecnológica ha forzadoa reescribir, versión a versión, el desarrollo inicial. La motivación a lahora de seleccionar una tecnología no puede ser nunca una moda o lainercia. Aunque cada año haya un nuevo lenguaje que ofrece versatilidady gran cantidad de facilidades, hay que tener siempre en mente queun lenguaje puede estar orientado a resolver un problema determinadomás adecuadamente que otros. En este punto hay que ser mucho máspragmáticos que fanáticos.

Hay desarrollos que en una versión temprana se han abandonadocompletamente y se han recomenzado de otra forma. Ya sea con otroslenguajes, herramientas, librerías o frameworks. El hecho de toparse conimpedimentos tan grandes de salvar ha provocado que una reescrituradesde cero sea con frecuencia lo más simple y rápido.

Para ampliar el conocimiento y la posibilidad de elección, sobretodo ahora que se incrementa el número de sistemas concurrentes,de alta disponibilidad, tolerantes a fallos y que deben prestar unservicio continuo en la red de redes, el presente libro proporciona elconocimiento de lo que es Erlang, lo que es OTP, y lo que significan estas

Page 13: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 13/189

Introducción

xiii

nuevas herramientas que va ganando cada vez más relevancia en losentornos mencionados.

5. ¿A quién va dirigido este libro?Este libro está dirigido a todo aquél que quiera aprender a programaren un lenguaje funcional con control de concurrencia y distribuido.Permite igualmente ampliar el vocabulario de programación del lectorcon nuevas ideas sobre el desarrollo de programas y la resolución deproblemas. Esto se aplica tanto a los que comienzan a programar como alos que ya saben programar, y a aquellos que quieren saber qué puedehacer este lenguaje para tomarlo en consideración en las decisionestecnológicas de su empresa o proyecto.

Para los programadores neófitos ofrece una guía de aprendizaje base, unaforma rápida de adentrarse en el conocimiento del lenguaje que permitecomenzar a desarrollar directamente. Propone ejemplos, ejercicios ypreguntas que el programador puede realizar, resolver y responder.

Para el programador experimentado ofrece un nexo hacia un lenguajediferente, si el lector proviene del mundo imperativo, o bienrelativamente similar a otros vistos (si se tienen conocimientos deLisp, Scheme, Prolog o Haskell). Provee un acercamiento detallado a lasentrañas de un sistema desarrollado con una ideología concreta y paraun fin concreto. Incluso si ya se conoce Erlang supone un recorrido por lo

que ya se sabe, pero desde otro enfoque y con características o detallesque probablemente no se conozcan.

Para el desarrollador, analista o arquitecto, ofrece el punto de vistade una herramienta, un lenguaje y un entorno, en el que se puedendesarrollar un cierto abanico de soluciones de forma rápida y segura.Erlang es un lenguaje con muchos años de desarrollo, probado enproducción por muchas empresas conocidas y desconocidas. Permiterealizar un recorrido por las potencias del lenguaje y obtener elconocimiento de sus debilidades. Lo suficiente como para saber si es unabuena herramienta para desarrollar una solución específica.

6. Estructura de la colecciónAl principio pensé en escribir un único libro orientado a Erlang, peroviendo el tamaño que estaba alcanzando pensé que mejor era dividirlopor temática y darle a cada libro la extensión apropiada como para serleído y consultado de forma fácil y rápida.

La colección, por tanto, consta de dos volúmenes. Cada volumen tienecomo misión explorar Erlang de una forma diferente, desde un punto devista diferente, y con un objetivo diferente. Los volúmenes son:

Page 14: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 14/189

Introducción

xiv

• Un mundo concurrente. En esta parte nos centraremos en conocer lasintaxis del lenguaje, sus elementos más comunes, sus estructuras, lostipos de datos, el uso de los ficheros y comunicaciones a través de la

red. Será el bloque más extenso, ya que detalla toda la estructura dellenguaje en sí.

• Las bases de OTP. Nos adentramos en el conocimiento del sistema OTP,el framework actualmente más potente para Erlang y que viene con suinstalación base. Se verán los generadores de servidores, las máquinasde estados, los supervisores y manejadores de eventos, entre otroselementos.

Nota

Recomiendo que, para poder hacer los ejemplos y practicar loque se va leyendo, se tenga a mano un ordenador con Erlanginstalado, así como acceso a su consola y un directorio en elque poder ir escribiendo los programas de ejemplo. En estecaso será de bastante ayuda revisar los apéndices donde explicacómo se descarga, instala y usa la consola de Erlang, así como lacompilación de los ejemplos y su ejecución de forma básica.

7. Nomenclatura usadaA lo largo del libro encontrarás muchos ejemplos y fragmentos de código.

Los códigos aparecen de una forma visible y con un formato distinto aldel resto del texto. Tendrán este aspecto:

-module(hola).

mundo() ->io:format("Hola mundo!~n", []).

Además de ejemplos con código Erlang, en los distintos apartados dellibro hay diferentes bloques que contienen notas informativas o avisosimportantes. Sus formatos son los siguientes:

NotaEsta es la forma que tendrán las notas informativas. Contienendetalles o información adicional sobre el texto para satisfacer lacuriosidad del lector.

Importante

Estas son las notas importantes que indican usos específicosy detalles importantes que hay que tener muy en cuenta. Serecomienda su lectura.

Page 15: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 15/189

Introducción

xv

8. Agradecimientos

Manuel Ángel RubioAgradecer a mi familia, Marga, Juan Antonio y Ana María, por serpacientes y dejarme el tiempo suficiente para escribir, así comosu amor y cariño. A mis padres por enseñarme a defenderme enesta vida, así como a competir conmigo mismo para aprender ysuperarme en cada reto personal y profesional.

Respecto al libro, he de agradecer al equipo con el que estuvetrabajando en Jet Multimedia: Guillermo Rodríguez, María Luisa dela Serna, Jonathan Márquez, Margarita Ortiz y Daniel López; el quecada desarrollo que nos planteasen pudiésemos verlo como undesafío a nosotros mismos y sacar lo mejor de nosotros mismos,así como aprender de cada situación, de cada lenguaje y de cadaherramienta. Aprendí mucho con ellos y espero que podamos seguiraprendiendo allá donde nos toque estar y, si volvemos a coincidir,muchísimo mejor.

También agradecer a José Luis Gordo por su revisión, el prólogoescrito y sus buenos consejos así como su crítica constructiva, hasido un aliado inestimable en esta aventura y un balón de oxígenoen momentos arduos.

A Juan Sebastián Pérez, por brindarse también a aprender ellenguaje de manos de este manuscrito, así como corregir tambiénmi forma de expresarme en algunos puntos que confieso fueroncomplicados.

Por último pero no por ello menos importante, agradecer a mihermano Rafael y a Luz (Bethany Neumann) el diseño de la portaday contraportada del libro, así como el logotipo de BosqueViejo.

José Luis Gordo

Agradecer a Manuel Angel su confianza por haberme dejado aportarmi pequeño granito de arena a este proyecto. Además de ampliarconocimientos, me ha dado la oportunidad de conocer mejor sutrabajo y a él personalmente, descubriendo su increíble energía ymotivación, sin la cual este libro nunca hubiera visto la luz.

Juan Sebastián Pérez

Gracias a Manuel por compartir tantos cafés (descafeinados) e ideas.A otros compañeros en lo profesional y personal, especialmente aldepartamento de movilidad de Jet Multimedia por compartir fatigasy éxitos. Y como no, a mi familia, amigos y pareja que me han

Page 16: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 16/189

Introducción

xvi

apoyado en el desarrollo de mis habilidades en otros aspectos dela vida.

9. Más información en la webPara obtener información sobre las siguientes ediciones, fe de erratasy comentarios, contactos, ayuda y demás sobre el libro Erlang/OTP hehabilitado una sección en mi web.

El sitio web:

http://erlang.bosqueviejo.net

Page 17: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 17/189

1

Capítulo 1. Lo que debes saber

sobre ErlangSoftware para un mundo concurrente.

—Joe Armstrong 

Erlang comienza a ser un entorno y un lenguaje de moda. La existenciacreciente de empresas orientadas a la prestación de servicios porinternet con un elevado volumen de transacciones (como videojuegosen red o sistemas de mensajería móvil y chat) hace que en sitios comolos Estados Unidos, Reino Unido o Suecia proliferen las ofertas de trabajoque solicitan profesionales en este lenguaje. Existe una necesidad

imperiosa de desarrollar entornos con las características de la máquinade Erlang, y la metodología de desarrollo proporcionada por OTP.

En este capítulo introducimos el concepto de Erlang y OTP. Su significado,características e historia. La información de este primer capítulo secompleta con las fuentes que lo han motivado y se provee informaciónprecisa sobre dónde se ha extraído cada sección.

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 sóloel compilador para poder ejecutar el código, sino que posee también unacolección de herramientas, y una máquina virtual sobre la que ejecutarlo,por lo tanto existen dos enfoques:

Erlang como lenguaje

Hay muchas discusiones concernientes a si Erlang es o no unlenguaje funcional. En principio, está entendido que sí lo es, aunquetenga elementos que le hagan salirse de la definición pura. Porello Erlang podría mejor catalogarse como un lenguaje híbrido, al

tener elementos de tipo funcional, de tipo imperativo, e inclusoalgunos rasgos que permiten cierta orientación a objetos, aunqueno completa.

Donde encaja mejor Erlang, al menos desde mi punto de vista, escomo un lenguaje orientado a la concurrencia. Erlang tiene una granfacilidad para la programación distribuida, paralela o concurrente yademás con mecanismos para la tolerancia a fallos. Fue diseñadodesde un inicio para ejecutarse de forma ininterrumpida. Estosignifica que se puede cambiar el código de sus aplicaciones sindetener su ejecución. Más adelante explicaremos cómo funcionaesto concretamente.

Page 18: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 18/189

Lo que debessaber sobre Erlang

2

Erlang como entorno de ejecución

Como hemos mencionado antes Erlang es una plataforma de

desarrollo que proporciona no sólo un compilador, sino tambiénuna máquina virtual para su ejecución. A diferencia de otroslenguajes interpretados como Python, Perl, PHP o Ruby, Erlang sepseudocompila y su máquina virtual le proporciona una importantecapa de abstracción que le dota de la capacidad de manejar ydistribuir procesos entre nodos de forma totalmente transparente(sin el uso de librerías específicas).

La máquina virtual sobre la que se ejecuta el código pseudo-compilado de Erlang, que le proporciona todas las características dedistribución y comunicación de procesos, es también una máquina

que interpreta un pseudocódigo máquina

1

que nada tiene que ver,a ese nivel, con el lenguaje Erlang. Esto ha permitido la proliferaciónde los lenguajes que emplean la máquina virtual pero no el lenguajeen sí, como pueden ser: Reia, Elixir, Efene, Joxa o LFE.

Erlang fue propietario hasta 1998 momento en que fue cedido comocódigo abierto (open source) a la comunidad. Fue creado inicialmente porEricsson, más específicamente por Joe Armstrong, aunque no sólo por él.

Recibe el nombre de Agnus Kraup Erlang. A veces se piensa que elnombre es una abreviación de ERicsson LANGuage, debido a su uso

intensivo en Ericsson. Según Bjarne Däcker, jefe del Computer ScienceLab en su día, esta dualidad es intencionada.

2. Características de Erlang

Durante el período en el que Joe Armstrong y sus compañeros estuvieronen los laboratorios de Ericsson, vieron que el desarrollo de aplicacionesbasadas en PLEX no era del todo óptimo para la programación deaplicaciones dentro de los sistemas hardware de Ericsson. Por esta razóncomenzaron a buscar lo que sería un sistema de desarrollo óptimo

basado en las siguiente premisas:Distribuido

El sistema debía de ser distribuido para poder balancear su cargaentre los sistemas hardware. Se buscaba un sistema que pudieralanzar procesos no sólo en la máquina en la que se ejecuta, sinoque también fuera capaz de hacerlo en otras máquinas. Lo que enlenguajes como C viene a ser PVM o MPICH pero sin el uso explícitode ninguna librería.

1O trozos de código nativo si se emplea HiPE.

Page 19: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 19/189

Lo que debessaber sobre Erlang

3

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 softwarecomo PLEX o C, un fallo en el código determina una interrupcióncompleta del programa con todos sus hilos y procesos. Hay otroslenguajes como Java, Python o Ruby que manejan estos errorescomo excepciones, afectando sólo a una parte del programa yno a todos sus hilos. No obstante, en los entornos con memoriacompartida, un error puede dejar corrupta esta memoria por loque esa opción no garantiza tampoco que no afecte al resto delprograma.

Escalable

Los sistemas operativos convencionales tenían problemas enmantener un elevado número de procesos en ejecución. Lossistemas de telefonía que desarrolla Ericsson se basan en tenerun proceso por cada llamada entrante, que vaya controlando losestados de la misma y pueda provocar eventos hacia un manejador,a su vez con sus propios procesos. Por lo que se buscaba un sistemaque pudiese gestionar desde cientos de miles, hasta millones deprocesos.

Cambiar el código en caliente

También es importante en el entorno de Ericsson, y en la mayoría

de sistemas críticos o sistemas en producción de cualquier índole,que el sistema no se detenga nunca, aunque haya que realizaractualizaciones. Por ello se agregó también como característica elhecho de que el código pudiese cambiar en caliente, sin necesidadde parar el sistema y sin que afectase al código en ejecución.

También había aspectos íntimos del diseño del lenguaje que se quisierontener en cuenta para evitar otro tipo de problemas. Aspectos tansignificativos como:

Asignaciones únicas

Como en los enunciados matemáticos la asignación de un valor auna variable se hace una única vez y, durante el resto del enunciado,esta variable mantiene su valor inmutable. Esto nos garantiza unmejor seguimiento del código y una mejor detección de errores.

Lenguaje simple

Para rebajar la curva de aprendizaje el lenguaje debe de tener pocoselementos y ninguna excepción. Erlang es un lenguaje simple decomprender y aprender, ya que tiene nada más que dos estructurasde control, carece de bucles y emplea técnicas como la recursividady modularización para conseguir algoritmos pequeños y eficientes.

Page 20: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 20/189

Lo que debessaber sobre Erlang

4

Las estructuras de datos se simplifican también bastante y supotencia, al igual que en lenguajes como Prolog o Lisp, se basa enlas listas.

Orientado a la Concurrencia

Como una especie de nueva forma de programar, este lenguaje seorienta a la concurrencia de manera que las rutinas más íntimasdel propio lenguaje están preparadas para facilitar la realización deprogramas concurrentes y distribuidos.

Paso de mensajes en lugar de memoria compartida

Uno de los problemas de la programación concurrente es laejecución de secciones críticas de código para acceso a porciones

de memoria compartida. Este control de acceso acaba siendo uncuello de botella ineludible. Para simplificar e intentar eliminarel máximo posible de errores, Erlang/OTP se basa en el pasode mensajes en lugar de emplear técnicas como semáforoso monitores. El paso de mensajes hace que un proceso seael responsable de los datos y la sección crítica se encuentresólo en este proceso, de modo que cualquiera que pidaejecutar algo de esa sección crítica, tenga que solicitárseloal proceso en cuestión. Esto abstrae al máximo la tarea dedesarrollar programas concurrentes, simplificando enormementelos esquemas y eliminando la necesidad del bloque explícito.

Hace no mucho encontré una presentación bastante interesante sobreErlang2, en la que se agregaba, no sólo todo lo que comentaba Armstrongque debía de tener su sistema para poder desarrollar las solucionesde forma óptima, sino también la contraposición, el porqué no lo pudoencontrar en otros lenguajes.

En principio hay que entender que propósito general se refiere al usogeneralizado de un lenguaje a lo más cotidiano que se suele desarrollar.Como es obvio, es más frecuente hacer un software para administraciónde una empresa que un sistema operativo. Los lenguajes de propósito

general serán óptimos para el desarrollo general de ese software degestión empresarial, seguramente no tanto para ese software del sistemaoperativo. PHP por ejemplo, es un fabuloso lenguaje de marcas quefacilita bastante la tarea a los desarrolladores web y sobre todo amaquetadores que se meten en el terreno de la programación. Pero esalgo completamente desastroso para el desarrollo de aplicaciones descripting para administradores de sistemas.

En sí, los lenguajes más difundidos hoy en día, como C# o Java, presentanel problema de carecer de elementos a bajo nivel integrados en sus

2 http://www.it.uu.se/edu/course/homepage/projektDV/ht05/uppsala.pdf 

Page 21: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 21/189

Lo que debessaber sobre Erlang

5

sistemas que les permitan desarrollar aplicaciones concurrentes deforma fácil. Esta es la razón de que en el mundo Java comience a hacersecada vez más visible un lenguaje como Scala.

3. Historia de Erlang

Joe Armstrong asistió a la conferencia de Erlang Factory de Londres, en2010, donde explicó la historia de la máquina virtual de Erlang. En sí,es la propia historia de Erlang/OTP. Sirviéndome de las diapositivas3 queproporcionó 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 había surgido en su plataforma AXE, que estaba siendodesarrollada en PLEX, un lenguaje propietario. Joe Armstrong junto a doscolegas, Elshiewy y Robert Virding, desarrollaron una lógica concurrentede programación para canales de comunicación. Esta álgebra de telefoníapermitía a través de su notación describir el sistema público de telefonía(POTS) en tan sólo quince reglas.

A través del interés de llevar esta teoría a la práctica desarrollaronmodelos en Ada, CLU, Smalltalk y Prolog entre otros. Así descubrieronque el álgebra telefónica se procesaba de forma muy rápida en sistemasde alto nivel, es decir, en Prolog, con lo que comenzaron a desarrollar un

sistema determinista en él.La conclusión a la que llegó el equipo fue que, si se puede resolverun problema a través de una serie de ecuaciones matemáticas yportar ese mismo esquema a un programa de forma que el esquemafuncional se respete y entienda tal y como se formuló fuera delentorno computacional, puede ser fácil de tratar por la gente queentiende el esquema, incluso mejorarlo y adaptarlo. Las pruebasrealmente se realizan a nivel teórico sobre el propio esquema, yaque algorítmicamente es más fácil de probarlo con las reglas propiasde las matemáticas que computacionalmente con la cantidad de

combinaciones que pueda tener.Prolog no era un lenguaje pensado para concurrencia, por lo quese decidieron a realizar uno que satisfaciera todos sus requisitos,basándose en las ventajas que habían visto de Prolog para conformarsu base. Erlang vió la luz en 1986, después de que Joe Armstrong seencerrase a desarrollar la idea base como intérprete sobre Prolog, conun número reducido de instrucciones que rápidamente fue creciendogracias a su buena acogida. Básicamente, los requisitos que se buscabancumplir eran:

3 http://www.erlang-factory.com/upload/presentations/247/erlang_vm_1.pdf 

Page 22: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 22/189

Lo que debessaber sobre Erlang

6

• Los procesos debían de ser una parte intrínseca del lenguaje, no unalibrería o framework de desarrollo.

• Debía poder ejecutar desde miles a millones de procesos concurrentesy cada proceso ser independiente del resto, de modo que si alguno deellos se corrompiese no dañase el espacio de memoria de otro proceso.Este requisito nos lleva a que el fallo de los procesos debe de seraislado del resto del programa.

• Debe poder ejecutarse de modo ininterrumpido, lo que obliga a quepara actualizar el código del sistema no se deba detener su ejecución,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 laconclusión de que el lenguaje era adecuado para la programación quese realizaba, pero tendría que ser, al menos unas 40 veces más rápido.

Mike Williams se encargó de escribir el emulador, cargador, planificador yrecolector de basura (en lenguaje C) mientras que Joe Armstrong escribíael compilador, las estructuras de datos, el heap de memoria y la pila; porsu parte Robert Virding se encargaba de escribir las librerías. El sistemadesarrollado se optimizó a un nivel en el que consiguieron aumentar surendimiento en 120 veces de lo que lo hacía el intérprete en Prolog.

En los años 90, tras haber conseguido desarrollar productos de lagama AXE con este lenguaje, se le potenció agregando elementos comodistribución, estructura OTP, HiPE, sintaxis de bit o compilación depatrones para matching . Erlang comenzaba a ser una gran pieza desoftware, pero tenía varios problemas para que pudiera ser adoptado deforma amplia por la comunidad de programadores. Desafortunadamentepara el desarrollo de Erlang, aquél periodo fue también la década de Javay Ericsson decidió centrarse en lenguajes usados globalmente por lo queprohibió seguir desarrollando en Erlang.

Nota

HiPE es el acrónimo de High Performance Erlang (Erlang de AltoRendimiento) que es el nombre de un grupo de investigaciónsobre Erlang formado en la Universidad de Uppsala en 1998. Elgrupo desarrolló un compilador de código nativo de modo que lamáquina (BEAM) virtual de Erlang no tenga que interpretar ciertaspartes del código si ya están en lenguaje máquina mejorando asísu rendimiento.

Con el tiempo, la imposición de no escribir código en Erlang se fueolvidando y la comunidad de programadores de Erlang comenzó a crecerfuera de Ericsson. El equipo OTP se mantuvo desarrollando y soportando

Page 23: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 23/189

Lo que debessaber sobre Erlang

7

Erlang que, a su vez, continuó como sufragador del proyecto HiPE yaplicaciones como EDoc o Dialyzer.

Antes de 2010 Erlang agregó capacidad para SMP y más recientementepara multi-core. La revisión de 2010 del emulador de BEAM se ejecutacon un rendimiento 300 veces superior al de la versión del emulador enC, por lo que es 36.000 veces más rápido que el original interpretado enProlog. Cada vez más sectores se hacen eco de las capacidades de Erlangy cada vez más empresas han comenzado desarrollos en esta plataformapor lo que se augura que el uso de este lenguaje siga al alza.

4. Desarrollos con Erlang

Los desarrollos en Erlang cada vez son más visibles para todos sobre todoen el entorno en el que Erlang se mueve: la concurrencia y la gestiónmasiva de eventos o elementos sin saturarse ni caer. Esto es un puntoesencial y decisivo para empresas que tienen su nicho de negocio enInternet y que han pasado de vender productos a proveer servicios através de la red.

En esta sección veremos la influencia de Erlang y cómo se va asentandoen el entorno empresarial y en las comunidades de software libre y eltipo de implementaciones que se realizan en uno y otro ámbito.

4.1. Sector empresarialDebido a las ventajas intrínsecas del lenguaje y su entorno se ha hechopatente la creación de modelos reales MVC para desarrollo web. Merecenmención elementos tan necesarios como ChicagoBoss o Nitrogen, cuyouso pueden verse en empresas como la española Tractis.

También es conocido el caso de Facebook que emplea Erlang en suimplementación de chat para soportar los mensajes de sus 70 millonesde usuarios. Al igual que Tuenti que también emplea esta tecnología.

La empresa inglesa Demonware4

, especializada en el desarrolloy mantenimiento de infraestructura y aplicaciones servidoras paravideojuegos en Internet, comenzó a emplear Erlang para poder soportarel número de jugadores de títulos tan afamados como Call of Duty.

Varias empresas del sector del entretenimiento que fabricanaplicaciones móviles también se han sumado a desarrollar susaplicaciones de parte servidora en Erlang/OTP. Un ejemplo de este tipode empresas es Wooga5.

4 http://www.erlang-factory.com/conference/London2011/speakers/MalcolmDowse5 http://es.slideshare.net/wooga/erlang-the-big-switch-in-social-games

Page 24: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 24/189

Lo que debessaber sobre Erlang

8

WhatsApp, la aplicación actualmente más relevante para el intercambioy envío de mensajes entre smartphones  emplea a nivel de servidorsistemas desarrollados en Erlang.

Una de las empresas estandarte de Erlang ha sido Kreditor, que cambiósu nombre a Klarna AB6. Esta empresa se dedica al pago por Internet ypasó en 7 años a tener 600 empleados.

En el terreno del desarrollo web comienzan a abrirse paso tambiénempresas españolas como Mikoagenda7. Es un claro ejemplo dedesarrollo de aplicaciones web íntegramente desarrolladas con Erlang anivel de servidor.

Desde que surgió el modelo Cloud , cada vez más empresas de software

están prestando servicios online en lugar de vender productos, por loque se enfrentan a un uso masificado por parte de sus usuarios, e inclusoa ataques de denegación de servicio. Estos escenarios junto con serviciosbastante pesados e infraestructuras no muy potentes hacen cada vez másnecesarias herramientas como Erlang.

En la web de Aprendiendo Erlang8 mantienen un listado mixto desoftware 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 mayoría de ellos se centra enentornos en los que se saca gran ventaja de la gestión de concurrencia ydistribución que realiza el sistema de Erlang.

Nota

Aprovechando que se ha comenzado a hacer esta lista de softwarelibre desarrollado en Erlang se ha estructurado y ampliado lapágina correspondiente a Erlang en Wikipedia (en inglés de

momento y poco a poco en castellano), por lo que en estosmomentos será más extensa que la lista presente en estaspáginas.

El siguiente listado se muestra como ejemplo:

• Base de Datos Distribuidas

6 https://klarna.com/7 https://mikoagenda.com/es8 http://aprendiendo-erlang.blogspot.com/p/donde-se-usa-erlang.html

Page 25: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 25/189

Lo que debessaber sobre Erlang

9

• Apache CouchDB9, es una base de datos documental con acceso adatos mediante HTTP y empleando el formato REST. Es uno de losproyectos que están acogidos en la fundación Apache.

• Riak10, una base de datos NoSQL inspirada en Dynamo (la basede datos NoSQL de Amazon). Es usada por empresas como Mozillay Comcast. Se basa en una distribución de fácil escalado ycompletamente tolerante a fallos.

• SimpleDB11, tal y como indica su propia web (en castellano) es unalmacén de datos no relacionales de alta disponibilidad flexible quedescarga el trabajo de administración de las bases de datos. Es decir,un sistema NoSQL que permite el cambio en caliente del esquemade datos de forma fácil que realiza auto-indexación y permite ladistribución de los datos. Fue desarrollada por Amazon.

• Couchbase12, es una base de datos NoSQL para sistemas demisión crítica. Con replicación, monitorización, tolerante a fallos ycompatible con Memcached.

• Servidores Web

• Yaws13. Como servidor web completo, con posibilidad de instalarsey configurarse para ello, sólo existe (al menos es el más conocido enla comunidad) Yaws. Su configuración se realiza de forma bastantesimilar a Apache. Tiene unos scripts que se ejecutan a nivel de

servidor bastante potentes y permite el uso de CGI y FastCGI.• Frameworks Web

• ErlyWeb14, no ha tenido modificaciones por parte de Yariv desdehace unos años por lo que su uso ha decaído. El propio Yariv loempleó para hacer un clon de twitter y se empleó inicialmente parala interfaz de chat para facebook.

• BeepBeep15, es un framework inspirado en Rails y Merb aunque sinintegración con base de datos.

• Erlang Web16, es un sistema desarrollado por Erlang Solutions quetrata igualmente las vistas y la parte del controlador pero tampocola parte de la base de datos.

9 http://couchdb.apache.org10 http://wiki.basho.com/Riak.html11 http://aws.amazon.com/es/simpledb/12 http://www.couchbase.com/13 http://yaws.hyber.org/14 https://github.com/yariv/erlyweb15 https://github.com/davebryson/beepbeep/16 http://www.erlang-web.org/

Page 26: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 26/189

Lo que debessaber sobre Erlang

10

• Nitrogen17, es un framework pensado para facilitar la construcciónde interfaces web. Nos permite agregar código HTML de una formasimple y enlazarlo con funcionalidad de JavaScript sin necesidad de

escribir ni una sola línea de código JavaScript.

• ChicagoBoss18, quizás el más activo y completo de los frameworksweb para Erlang a día de hoy. Tiene implementación de vistas,plantillas (ErlyDTL), definición de rutas, controladores y modelos através de un sistema ORM19.

• CMS (Content Management System)

• Zotonic20, sistema CMS21 que permite el diseño de páginas web deforma sencilla a través de la programación de las vistas (DTL) y la

gestión del contenido multimedia, texto y otros aspectos a través delinterfaz de administración.

• Chat

• ejabberd22, servidor de XMPP muy utilizado en el mundo Jabber.Este servidor permite el escalado y la gestión de multi-dominios. Esusado en sitios como la BBC Radio LiveText, Ovi de Nokia, KDE Talk,Chat de Facebook, Chat de Tuenti, LiveJournal Talk, etc.

• Colas de Mensajes

• RabbitMQ23, servidor de cola de mensajes muy utilizado en sistemasde entornos web con necesidad de este tipo de sistemas paraconexiones de tipo websocket, AJAX o similar en la que se haganecesario un comportamiento asíncrono sobre las conexionessíncronas. Fue adquirido por SpringSource, una filial de VMWare enabril de 2010.

5. Erlang y la Concurrencia

Una de las mejores pruebas de que Erlang/OTP funciona, es mostrar lascomparaciones que empresas como Demonware o gente como el propioJoe Armstrong han realizado. Sistemas sometidos a un banco de pruebas

17 http://nitrogenproject.com/18 http://www.chicagoboss.org/19Object Relational Mapping , sistema empleado para realizar la transformación entre objetos y tablaspara emplear directamente los objetos en código y que la información que estos manejen se almaceneen una tabla de la base de datos.20 http://zotonic.com/21Content Management System, Sistema de Administración de Contenido22 http://www.ejabberd.im/23 http://www.rabbitmq.com/

Page 27: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 27/189

Lo que debessaber sobre Erlang

11

para comprobar cómo rinden en producción real o cómo podrían rendiren entornos de pruebas controlados.

Comenzaré por comentar el caso de la empresa Demonware, de la queya comenté algo en la sección de uso de Erlang en el Sector empresarial,pero esta vez lo detallaré con datos que aportó la propia compañía através de Malcolm Dowse en la Erlang Factory de Londrés de 2011.

Después veremos el banco de pruebas que realizó Joe Armstrong sobreun servicio empleando un par de configuraciones de Apache y Yaws.

5.1. El caso de Demonware

En la conferencia de Erlang Factory de Londrés, en 2011, Malcolm Dowse,

de la empresa Demoware (de Dublín), 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 cargade Erlang.

Demonware es la empresa que trabaja con Activision y Blizzard dandosoporte de los servidores de juegos multi-jugador XBox y PlayStation.La empresa se constituyó en 2003 y desde esa época hasta 2007 semantuvieron modificando su tecnología 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 situación de superar esa cifra.Además, el código se colgaba con frecuencia, lo que suponía un graveproblema.

En 2006 se reescribió toda la lógica de negocio en Python. Se seguíamanteniendo a nivel interno C++ con lo que el código se había hechodifícil de mantener.

Finalmente, en 2007, se reescribió el código de los servidores de C++ conErlang. Fueron unos 4 meses de desarrollo con el que consiguieron queel sistema ya no se colgase, que se mejorase y facilitase la configuracióndel sistema (en la versión C++ era necesario reiniciar para reconfigurar,lo que implicaba desconectar a todos los jugadores). También se dotóde mejores herramientas de log y administración y se hacía más fácildesarrollar nuevas características en muchas menos líneas de código.Para entonces habían llegado a los 20 mil usuarios concurrentes.

A finales de 2007 llegó Call of Duty 4, que supuso un crecimientoconstante de usuarios durante 5 meses continuados. Se pasó de 20 mila 2,5 millones de usuarios. De 500 a 50 mil peticiones por segundo. Laempresa tuvo que ampliar su nodo de 50 a 1850 servidores en varios

Page 28: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 28/189

Lo que debessaber sobre Erlang

12

centros de datos. En palabras de Malcolm: fue una crisis para la compañía,teníamos que crecer, sin el cambio a Erlang la crisis podría haber sido undesastre .

Demonware es una de las empresas que ha visto las ventajas de Erlang.La forma en la que implementa la programación concurrente y la grancapacidad de escalabilidad. Gracias a estos factores, han podido estar a laaltura de prestar el servicio de los juegos en línea más usados y jugadosde los últimos tiempos.

5.2. Yaws contra Apache

Es bastante conocido ya el famoso gráfico24 sobre la comparativa querealizaron Joe Armstrong y Ali Ghodsi entre Apache y Yaws. La prueba es

bastante fácil, de un lado, un servidor, de otro, un cliente para medicióny 14 clientes para generar carga.

La prueba propuesta era generar un ataque de denegación de servicio(DoS), que hiciera que los servidores web, al recibir un número depeticiones excesivo, fuesen degradando su servicio hasta dejar de darlo.Es bien conocido que este hecho pasa con todos los sistemas, ya que losrecursos de un servidor son finitos. No obstante, por su programación,pueden pasar cosas como las que se visualizan en el gráfico:

En gris oscuro (marcando el punto con un círculo y ocupando las líneassuperiores del gráfico) puede verse la respuesta de Yaws en escala de

24 http://www.sics.se/~joe/apachevsyaws.html

Page 29: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 29/189

Lo que debessaber sobre Erlang

13

KB/s (eje Y) frente a carga (eje X). Las líneas que se cortan a partir de las 4mil 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 secciónanterior, Apache no puede procesar más de 4000 peticiones simultáneas,en parte debido a su integración íntimamente ligada al sistema operativo,que le limita. Sin embargo, Yaws se mantiene con el mismo rendimientohasta llegar a superar las 80 mil peticiones simultáneas.

Erlang está construido con gestión de procesos propia y desligada delsistema operativo. En sí, suele ser más lenta que la que proporciona elsistema operativo, pero sin duda la escalabilidad y el rendimiento que seconsigue pueden paliar ese hecho. Cada nodo de Erlang puede manejar

en total unos 2 millones de procesos.

Page 30: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 30/189

14

Capítulo 2. El lenguajeSólo 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 terminagustándole y otras personas que lo consideran incómodo. Hay queentender que es un lenguaje basado en Prolog y con tintes de Lisp por loque se asemeja más a los lenguajes funcionales que a los imperativos.

La mayoría de personas comienzan programando en lenguajes comoBasic, 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 tienenuna sintaxis, el uso de los bloques condicionales, iterativos y declaraciónde funciones y clases también semejantes.

En los lenguajes imperativos la sintaxis se basa en la consecución demandatos que el programador envía a través del código a la máquina. EnErlang y demás lenguajes funcionales, la sintaxis está diseñada como sise tratara de la definición de una función matemática o una proposiciónlógica. Cada elemento dentro de la función tiene un propósito: obtenerun valor; el conjunto de todos esos valores, con o sin procesamiento,conforma el resultado. Un ejemplo básico:

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

En este ejemplo puede verse la definición de la función area . Losparámetros requeridos para obtener su resultado son Base  y Altura . Ala declaración de parámetros le sigue el símbolo de consecución (->),como si se tratase de una proposición lógica. Por último está la operacióninterna que retorna el resultado que se quiere obtener.

Al tratarse de funciones matemáticas o proposiciones lógicas no existeuna correlación entre imperativo y funcional. Para un código imperativocomún como el que sigue:

para i <- 1 hasta 10 hacersi clavar(i) = 'si' entonces

martillea_clavo(i)fsi

fpara

No existe en Erlang un equivalente que pueda transcribir una acciónimperativa como tal. Para desarrollar en Erlang hay que pensar en el qué se quiere hacer más que en el cómo. Si en un lenguaje funcional lo que sequiere es clavar los clavos que seleccione la función clavar martilleando,se podría hacer a través de una lista de comprensión:

Page 31: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 31/189

El lenguaje

15

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

Hay que entender que para resolver problemas de forma funcionalmuchas veces la mentalidad imperativa es un obstáculo. Tenemos quepensar en los datos que tenemos y qué datos queremos obtener comoresultado. Es lo que nos conducirá a la solución.

Erlang es un lenguaje de formato libre. Se pueden insertar tantosespacios y saltos de línea entre símbolos como se quiera. Esta funciónarea es completamente equivalente a la anterior a nivel de ejecución:

area(Base,Altura

) ->Base * Altura

.

A lo largo de este capítulo revisaremos la base del lenguaje Erlang.Veremos lo necesario para poder escribir programas básicos de propósitogeneral y entender esta breve introducción de una forma más detalladay clara.

1. Tipos de Datos

En Erlang se manejan varios tipos de datos. Por hacer una distinciónrápida podemos decir que se distinguen entre: simples y complejos;otras organizaciones podrían conducirnos a pensar en los datos como:escalares y conjuntos o atómicos y compuestos. No obstante, la formade organizarlos no es relevante con el fin de conocerlos, identificarlosy usarlos correctamente. Emplearemos la denominación simples ycomplejos (o compuestos), pudiendo referirnos a cualquiera de las otrasformas de categorización si la explicación resulta más clara.

Como datos simples veremos en esta sección los átomos y los números.Como datos de tipo complejo veremos las listas y tuplas. También

veremos las listas binarias, un tipo de dato bastante potente de Erlangy los registros, un tipo de dato derivado de las tuplas.

1.1. Átomos

Los átomos son identificadores de tipo carácter que se emplean comopalabras clave y ayudan a semantizar el código.

Un átomo es una palabra que comienza por una letra en minúscula y vaseguido de letras en mayúscula o minúscula, números y/o subrayados.También se pueden emplear letras en mayúscula al inicio, espacios y lo

Page 32: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 32/189

El lenguaje

16

que queramos, siempre y cuando encerremos la expresión entre comillassimples. 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 aidentificar estructuras, algoritmos y código específico.

Hay átomos que se emplean con mucha frecuencia como son: true, falsey undefined.

Los átomos junto con los números enteros y reales y las cadenas de textocomponen lo que se conoce en otros lenguajes como literales . Son los

datos que tienen un significado de por sí, y se pueden asignar a unavariable directamente.

Nota

Como literales se pueden especificar números, pero tambiénvalores de representaciones de la tabla de caracteres. Al igualque en otros lenguajes, Erlang permite dar el valor de un carácterespecífico a través el uso de la sintaxis: $A, $1, $!. Esto retornará elvalor numérico para el símbolo indicado tras el símbolo del dólaren la tabla de caracteres.

1.2. Números Enteros y Reales

En Erlang, los números pueden ser de dos tipos, tal y como se ve en esteejemplo de código en la consola:

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

Page 33: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 33/189

El lenguaje

17

true

Otra de las cosas que sorprende de Erlang es su precisión numérica.

Si multiplicamos números muy altos veremos como el resultado siguemostrándose en notación real, sin usar la notación científica quemuestran otros lenguajes cuando una operación supera el límite decálculo de los números enteros (o valores erróneos por overflow ):

> 102410241024 * 102410241024 * 1234567890.12947972063153419287126752624640

Esta característica hace de Erlang una plataforma muy precisa y adecuadapara cálculos de intereses bancarios, tarificación telefónica, índicesbursátiles, valores estadísticos, posición de puntos tridimensionales, etc.

Nota

Los números se pueden indicar también anteponiendo la baseen la que queremos expresarlos y usando como separador laalmohadilla (#). Por ejemplo, si queremos expresar los númerosen base octal, lo haremos anteponiendo la base al número quequeremos representar 8#124. Análogamente 2#1011 representaun número binario y 16#f42a un número hexadecimal.

1.3. Variables

Las variables, como en matemáticas, son símbolos a los que se enlaza unvalor y sólo uno a lo largo de toda la ejecución del algoritmo específico.Esto quiere decir que cada variable durante su tiempo de vida sólo puedecontener un valor.

El formato de las variables se inicia con una letra mayúscula, seguida detantas letras, números y subrayados como se necesiten o deseen. Unavariable puede tener esta forma:

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

Sobre las variables se pueden efectuar expresiones aritméticas, encaso de que contenga números, operaciones de listas o emplearsecomo parámetro en llamadas a funciones. Un ejemplo de variablesconteniendo números:

> Base = 2.2

Page 34: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 34/189

El lenguaje

18

> Altura = 5.2.5.2> Base * Altura.10.4

Si en un momento dado, queremos que Base tenga el valor 3 en lugar delvalor 2 inicialmente asignado veríamos 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 quela concordancia (o match) con el valor 2 es correcto, mientras que si lo

intentamos encajar con el valor 3 resulta en una excepción.

Nota

Para nuestras pruebas, a nivel de consola y para no tener que saliry entrar cada vez que queramos que Erlang olvide el valor con elque se enlazó una variable, podemos emplear:

> f(Base).ok> Base = 3.3

Para eliminar todas las variables que tenga memorizadas laconsola se puede emplear: f().

La ventaja de la asignación única es la facilidad de analizar códigoaunque muchas veces no se considere así. Si una variable durante todala ejecución de una función sólo puede contener un determinado valorel comportamiento de dicha función es muy fácilmente verificable1.

1.4. Listas

Las listas en Erlang son vectores de información heterogénea, es decir,pueden contener información de distintos tipos, ya sean números,átomos, tuplas u otras listas.

Las listas son una de las potencias de Erlang y otros lenguajesfuncionales. Al igual que en Lisp, Erlang maneja las listas como lenguajede alto nivel, en modo declarativo, permitiendo cosas como las listasde comprensión o la agregación y eliminación de elementos específicoscomo si de conjuntos se tratase.

1Muestra de ello es dialyzer , una buena herramienta para comprobar el código escrito en Erlang.

Page 35: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 35/189

Page 36: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 36/189

El lenguaje

20

[1]

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

dificulta su uso. Este asunto lo analizaremos más adelante con eltratamiento de los procesos y las funciones.

1.4.2. Cadenas de Texto

Las cadenas de texto son un tipo específico de lista. Se trata de unalista homogénea de elementos representables como caracteres. Erlangdetecta que si una lista en su totalidad cumple con esta premisa, es unacadena de caracteres.

Por tanto, la representación de la palabra Hola en forma de lista, se puedehacer como lista de enteros que representan a cada una de las letras ocomo el texto encerrado entre comillas dobles ("). Una demostración:

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

Como puede apreciarse, la asignación no da ningún error ya que ambosvalores, a izquierda y derecha, son el mismo para Erlang.

Importante

Esta forma de tratar las cadenas es muy similar a la que se empleaen lenguaje C, en donde el tipo de dato char  es un dato de 8bits en el que se puede almacenar un valor de 0 a 255 y quelas funciones de impresión tomarán como representaciones dela tabla de caracteres en uso por el sistema. En Erlang, la únicadiferencia es que cada dato no es de 8 bits sino que es unentero lo que conlleva un mayor consumo de memoria pero mejorsoporte de nuevas tablas como la de UTF-16 o las extensiones delUTF-8 y similares.

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

también la agregación de elementos, de modo que la concatenación sepodría realizar de la siguiente forma:

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

Una de las ventajas de la asignación propia de que dispone Erlang esque si encuentra una variable que no ha sido enlazada a ningún valor,automáticamente cobra el valor necesario para que la ecuación seacierta. Erlang intenta hacer siempre que los elementos a ambos lados delsigno de asignación sean iguales. Un ejemplo:

Page 37: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 37/189

El lenguaje

21

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

"mundo!"

Esta notación tiene sus limitaciones, en concreto la variable no asignadadebe estar al final de la expresión, ya que de otra forma el código pararealizar el encaje sería mucho más complejo.

1.4.3. Listas binarias

Las cadenas de caracteres se forman por conjuntos de enteros, esdecir, se consume el doble de memoria para una cadena de caracteresalmacenada en una lista en Erlang que en cualquier otro lenguaje. Las

listas binarias permiten almacenar cadenas de caracteres con tamaño debyte y permite realizar trabajos específicos con secuencias de bytes oincluso 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 vistasanteriormente. No se pueden agregar elementos ni emplear el formatode anexión y supresión de elementos tal y como se había visto antes.Pero se puede hacer de otra forma más potente.

Por ejemplo, la forma en la que tomábamos la cabeza de la lista en unavariable y el resto lo dejábamos en otra variable, se puede simular de lasiguiente forma:

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

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

La concatenación en el caso de las listas binarias no se realiza comocon las listas normales empleando el operador ++. En este caso deberealizarse de la siguiente forma:

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

Page 38: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 38/189

El lenguaje

22

<<"Hola mundo!">>

Para obtener el tamaño de la lista binaria empleamos la funciónbyte_size/1. En el caso anterior para cada una de las variablesempleadas:

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

Esta sintaxis es un poco más elaborada que la de las listas, pero se debea que nos adentramos en la verdadera potencia que tienen las listasbinarias: el manejo de bits.

1.4.4. Trabajando con Bits

En la sección anterior vimos la sintaxis básica para simular elcomportamiento de la cadena al tomar la cabeza de una pila. Esta sintaxisse basa en el siguiente formato: Var:Tamaño/Tipo; siendo opcionalesTamaño y Tipo.

El tamaño está ligado al tipo, ya que una unidad de medida no es nada sinsu 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 loque el tamaño será referente a cuántos elementos de la lista contendrála variable.

En caso de que el tamaño no se indique, se asume que es tanto comoel tipo soporte y/o hasta encajar el valor al que debe de igualarse (si esposible), por ello en el ejemplo anterior la variable T se queda con elresto de la lista binaria.

Los tipos también tienen una forma compleja de formarse, ya que sepueden indicar varios elementos para completar la definición de los

mismos. Estos elementos son, en orden de especificación: 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 leídos en la máquina, si es enformato Intel o Motorola, es decir, little o big respectivamente. Ademásde estos dos, es posible elegir native , que empleará el formato nativode la máquina en la que se esté ejecutando el código. El valor pordefecto se prefija big .

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

Page 39: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 39/189

El lenguaje

23

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

En este ejemplo se ve que la máquina de la prueba es de tipo big uordenación Intel.

• Signo: se indica si el número indicado se almacenará en formato consigno o sin él, es decir, signed o unsigned , respectivamente.

• Tipo: es el tipo con el que se almacena el dato en memoria. Según eltipo el tamaño es relevante para indicar precisión o número 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 tamaño.En caso de enteros y coma flotante el valor por defecto es 1, y encaso de binario es 8. Por lo tanto: Tamaño x Unidad = Número de bits ;por ejemplo, si la unidad es 8 y el tamaño es 2, los bits que ocupa elelemento son 16 bits.

Si quisiéramos almacenar tres datos de color rojo, verde y azul en 16bits, tomando para cada uno de ellos 5, 5 y 6 bits respectivamente,tendríamos que la partición de los bits se podría hacer de forma algodificultosa. Con este manejo de bits, componer la cadena de 16 bits (2bytes) correspondiente, por ejemplo, a los valores 20, 0 y 6, sería así:

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

Nota

Para obtener el tamaño de la lista binaria en bits podemosemplear la función bit_size/1 que nos retornará el tamaño dela lista binaria:

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

1.5. Tuplas

Las tuplas son tipos de datos organizativos en Erlang. Se pueden crearlistas de tuplas para conformar conjuntos de datos homogéneos deelementos individuales heterogéneos.

Las tuplas, a diferencia de las listas, no pueden incrementar nidecrementar su tamaño salvo por la redefinición completa de su

estructura. Se emplean para agrupar datos con un propósito específico.

Page 40: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 40/189

El lenguaje

24

Por ejemplo, imagina que tenemos un directorio con unos cuantosficheros. Queremos almacenar esta información para poder tratarla ysabemos que va a ser: ruta, nombre, tamaño y fecha de creación.

Esta información se podría almacenar en forma de tupla de la siguienteforma:

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

Las llaves indican el inicio y fin de la definición de la tupla, y loselementos separados por comas conforman su contenido.

Nota

En el ejemplo se puede ver que la fecha y hora se ha introducidode una forma un tanto peculiar. En Erlang, las funciones de losmódulos de su librería estándar, trabajan con este formato, y si seemplea, es más fácil tratar y trabajar con fechas. Por ejemplo, siejecutásemos:

> {date(), time()}.{{2011,12,6},{22,5,17}}

Este tipo de dato también se emplea para emular los arrays asociativos (o hash). Estos arrays almacenan información de forma que sea posiblerescatarla mediante el texto o identificador específico que se usó paraalmacenarla. Se usa en aquellos casos en que es más fácil que accederal elemento por un identificador conocido que por un índice que podríaser desconocido.

1.5.1. Listas de Propiedades

Una lista de propiedades es una lista de tuplas clave, valor. Se gestionamediante la librería proplists . Las listas de propiedades son muy usadaspara almacenar configuraciones o en general cualquier informaciónvariable 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 algúnfichero o mediante cualquier otro método, queremos consultar sidebemos de realizar o no la depuración del sistema, es decir, mostrarmensajes de log si la propiedad debug es igual a true :

> proplists:get_value(debug, A).

Page 41: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 41/189

El lenguaje

25

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 sería:

> 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 accesoa los datos que sabemos que existen dentro de una colección (o lista) yextraer únicamente los que queramos obtener.

Nota

El módulo de proplists contiene muchas más funciones útiles paratratar este tipo de colección de datos de forma fácil. No es mala

idea dar un repaso al mismo para ver el partido que podemossacarle en nuestros programas.

1.6. Registros

Los registros son un tipo específico de tupla que facilita el acceso a losdatos individuales dentro de la misma mediante un nombre y una sintaxisde acceso mucho más cómoda para el programador. Internamente paraErlang, los registros realmente no existen. A nivel de preprocesador sonintercambiados por tuplas. Esto quiere decir que los registros en sí sonuna simplificación a nivel de uso de las tuplas.

Como los registros se emplean a nivel de preprocesador, en la consolasólo podemos definir registros empleando un comando específico deconsola. Además, podemos cargar los registros existentes en un ficheroy emplearlos desde la propia consola para definir datos o para emplearlos comandos propios de manejo de datos con registros.

La definición de registros desde la consola se realiza de la siguienteforma:

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

Page 42: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 42/189

El lenguaje

26

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

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

Nota

Los ficheros de código de Erlang normalmente tiene la extensiónerl, sin embargo, cuando se trata de códigos de tipo cabecera ,estos ficheros mantienen una extensión a medio camino entrelos de cabecera de C (que tienen la extensión .h) y los de códigonormales de Erlang. Su extensión es: hrl. En estos ficheros seintroducirán normalmente definiciones y registros.

Veamos con una pequeña prueba que si creamos una tupla A Erlangla reconoce como tupla de cuatro elementos. Si cargamos después elarchivo registros.hrl cuyo contenido es la definición del registroagenda el tratamiento de la tupla se modifica automáticamente yya podemos emplear la notación para registros de los ejemplossubsiguientes:

> 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 registroy como cuenta con el mismo número de elementos, si no tenemos encuenta el identificador, la considera automáticamente como un registro.También se pueden seguir empleando las funciones y elementos típicosde la tupla ya que a todos los efectos sigue siéndolo.

Nota

Para obtener la posición dentro de la tupla de un campo, bastacon escribirlo de la siguiente forma:

#agenda.nombre

Esto nos retornará la posición relativa definida como nombre conrespecto a la tupla que contiene el registro de tipo agenda .

Para tratar los datos de un registro, podemos realizar cualquiera de lassiguientes acciones:

> A#agenda.nombre.

Page 43: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 43/189

El lenguaje

27

"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 asignación 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 enel ejemplo) y después agregaremos un punto y el nombre del campo alque queremos acceder.

Para modificar los datos de un registro existente en lugar del puntoemplearemos las llaves. Dentro de las llaves estableceremos tantasigualdades clave=valor como necesitemos (separadas por comas), tal ycomo se ve en el ejemplo anterior.

Para obtener en un momento dado información sobre los registros,podemos emplear la función record_info. Esta función tiene dosparámetros, el primero es un átomo que puede contener fields siqueremos que retorne una lista de átomos con el nombre de cada campo;o size, para retornar el número de campos que tiene la tupla donde

se almacena el registro (incluído el identificativo, en nuestros ejemplosagenda ).

Importante

Como se ha dicho anteriormente, los registros son entidadesque trabajan a nivel de lenguaje pero Erlang no los contemplaen tiempo de ejecución. Esto quiere decir que el preprocesadortrabaja para convertir cada instrucción concerniente a registrospara que sean relativas a tuplas y por tanto la función record_infono 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 contenera su vez cualquier otro tipo de dato, no sólo átomos, cadenas de textoo números, sino también otros registros, tuplas o listas. Con ello, estaestructura nos propone un sistema organizativo interesante para poderacceder directamente al dato que necesitemos en un momento dado

facilitando la labor del programador enormemente.

Page 44: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 44/189

El lenguaje

28

2. Imprimiendo por pantalla

Muchas veces se nos presentará la necesidad de mostrar datos porpantalla. De momento, toda la información que vemos es porque laconsola nos la muestra, como resultado de salida del código que vamosescribiendo. No obstante, hay momentos, en los que será necesariorealizar una salida concreta de un dato con información más completa.

Para ello tenemos el módulo io, del que emplearemos de momento sólola función  format . Esta función nos permite imprimir por pantalla lainformación que queramos mostrar basado en un formato específico quese pasa como primer parámetro.

NotaPara los que hayan programado con lenguajes tipo C, Java, PHP, ...esta función es equivalente y muy parecida a printf, es decir, lafunción se basa en una cadena de texto con un formato específico(agregando parámetros) que serán sustituidos por los valores quese indiquen en los parámetros 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 función es ok , por lo que se imprimela cadena de texto y seguidamente el retorno de la función (el retorno defunción se imprime siempre en consola). Para hacer un retorno de carro,debemos de insertar un caracter especial. A diferencia de otros lenguajesdonde se usan los caracteres especiales, Erlang no usa la barra invertida,sino que emplea la virgulilla (~), y tras este símbolo, los caracteres seinterpretan de forma especial. Tenemos:

~

Imprime el símbolo de la virgulilla.

c

Representa un carácter que será reemplazado por el valorcorrespondiente pasado en la lista como segundo parámetro. Antesde la letra c  se pueden agregar un par de números separadospor un punto. El primer número indica el tamaño del campo yla justificación a izquierda o derecha según el signo positivo onegativo del número. El segundo número indica las veces que serepetirá el caracter. Por ejemplo:

Page 45: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 45/189

El lenguaje

29

> 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 números en coma flotante. El formato dee es científico (X.Ye+Z) mientras que  f  lo presenta en formato concoma fija. El formato g es una mezcla ya que presenta el formatocientífico si el número se sale del rango [0.1,10000.0], y en casocontrario presenta el formato como si fuese e . Los números que sepueden anteponer a cada letra indican, el tamaño que se quiererepresentar y justificación (como se vió antes). Tras el punto laprecisión. 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 significadodel segundo número en este caso es la cantidad de caracteres de lalista 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 sobretodopara poder imprimir tuplas, pero imprime igualmente listas,números, átomos, etc. La única salvedad, es que una cadenade caracteres será considerada como una lista. Los números deanteposición 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 versión de W es similar a la anterior aunque toma dos parámetrosde la lista de parámetros. El primero es el dato que se va a imprimir,el segundo es la profundidad. Si imprimimos una lista con muchoselementos, podemos mostrar únicamente un número determinadode ellos. A partir de ese número agrega puntos suspensivos. Unejemplo:

Page 46: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 46/189

El lenguaje

30

> 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 decaracteres para imprimirla como tal. Si la impresión es demasiadogrande, la parte en varias líneas. La versión en mayúscula, tambiénes igual a su homónimo W , aceptando un parámetro extra paraprofundidad.

b / B / x / X / + / #

Imprimen números según la base indicada. Los números anterioresa cada letra (o símbolo) indican, el primero la magnitud y justificación de la representación y el segundo la base en la que seexpresará el número. La diferencia entre ellos es que B imprime sólola representación numérica.

Con X  se puede emplear un prefijo que se toma del siguienteparámetro que haya en la lista de parámetros, consecutivo al valora representar.

El símobolo de almohadilla (#) siempre antepone la baseen formato Erlang: 10#20 (decimal), 8#65 (octal), 16#1A(hexadecimal). La diferencia entre las mayúsculas y minúsculas

es precisamente esa, la representación de las letras de las basesmayores a 10 en mayúsculas o minúsculas. Un ejemplo:

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

i

Ignora el parámetro que toque emplear. Es útil si el formato delos parámetros que se pasa es siempre el mismo y en un formatoespecífico se desea ignorar uno concreto.

n

Retorno de carro, hace un salto de línea, de modo que se puedaseparar por líneas diferentes lo que se desee imprimir por pantalla.

Nota

Existe también el módulo io_lib que dispone también de lafunción format. La única diferencia que presenta, es que en lugarde presentar por pantalla la cadena resultante, la retorna comocadena de caracteres.

Page 47: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 47/189

El lenguaje

31

3. Fechas y Horas

El manejo de fechas y horas en Erlang no se realiza con un tipo estándar,sino que se establece como un término encerrado en una tupla. Unafecha tiene la siguiente forma de tupla:

{2012,5,22}

Es una tupla compuesta por tres campos enteros destinados al año, mesy día, en ese orden. La función interna date/0 retorna este formato, perohay más funciones de tratamiento de fecha que emplean este formato.

El tiempo también se maneja en una tupla de tres elementos en la que sepueden diferenciar en este orden: hora, minutos y segundos. Un ejemplosería el siguiente:

{22,10,5}

Una fecha y hora completa se representa a través de otra tupla quecontiene en su interior las tuplas mencionadas antes, separadas en doselementos 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 empleartambién estas otras funciones dentro de una tupla de dos elementos:{date(), time()}

Hay otras funciones como now/0, que retornan la fecha y hora actualesen formato POSIX2, en una tupla {MegaSeconds, Seconds, MicroSeconds} ,lo que quiere decir que el cálculo de la hora en un sólo entero sería así:

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

Por último, indicar que las fechas también pueden ser convertidaso empleadas en formato UTC (o GMT). Podemos convertir unafecha a formato UTC (erlang:localtime_to_universaltime/1) oviceversa (erlang:universaltime_to_localtime/1).

2El formato de POSIX para fecha y hora consiste en un número entero que corresponde al número desegundos transcurrido desde el 1 de enero de 1970 hasta la fecha que se indique.

Page 48: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 48/189

El lenguaje

32

Nota

El módulo calendar  provee una serie de funciones que

permiten averiguar si el año introducido es bisiesto(is_leap_year/1), el día de la semana de una fecha concreta(iso_week_number/0 e iso_week_number/1), el último díadel mes (last_day_of_the_month/2) y más aún.

Este módulo, además, tiene la capacidad de trabajar con segundosgregorianos en lugar de POSIX. El número obtenido en segundos(para representación interna) es contado desde el año cero3, enlugar de 1970. Esto da la posibilidad de dar fechas anteriores a1970.

3La toma de segundos siempre es en formato UTC (o GMT), por lo que las fechas que se proporcionen parala conversión a segundos, serán tomadas como en hora local y convertidas a UTC antes de su conversióna segundos.

Page 49: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 49/189

33

Capítulo 3. Expresiones,

Estructuras y ExcepcionesLa mejor forma de predecir el futuro es 

implementarlo.—David Heinemeier Hansson

En este capítulo ampliamos lo visto en el capítulo anterior con elconocimiento de las expresiones lógicas, las expresiones aritméticas, lasestructuras de control y el manejo de las excepciones.

1. ExpresionesLas expresiones son la conjunción de símbolos con datos para conformaruna sentencia válida para el lenguaje con significado para el compilador,de modo que pueda ofrecer, en tiempo de ejecución, una representacióna nivel de código máquina del resultado que se pretende obtener.

Las expresiones pueden ser de tipo aritmético o lógico. Las aritméticasbuscan un valor a través de operaciones matemáticas simples ocomplejas. De un conjunto de datos dados con las operaciones indicadasy el orden representado por la expresión se obtiene un resultado. En las

lógicas se busca una conclusión lógica (o binaria) a la conjunción de lospredicados expuestos.

1.1. Expresiones Aritméticas

Con los números, de forma nativa, se pueden llevar a cabo expresionesaritméticas. Las más básicas, como la suma, resta, multiplicación ydivisión son de sobra conocidas. Otras operaciones como la divisiónentera o el remanente (o módulo) se implementan en cada lenguaje deuna forma distinta, por lo que haremos un repaso rápido 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

Page 50: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 50/189

Expresiones, Estructurasy Excepciones

34

Se puede hacer uso de los paréntesis para establecer una relación deprecedencia de operadores para, por ejemplo, anteponer una suma a unamultiplicación. También se pueden realizar operaciones encadenadas,

por ejemplo multiplicando más de dos operandos. Ejemplos de todoesto:

> 2 * 3 + 1.7> 2 * (3 + 1).8> 3 * 3 * 3.27

1.2. Expresiones Lógicas

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 operadorestratan los números como binarios y operan con el valor de cada una desus 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 números.

También podemos encontrarnos con que queremos almacenar elresultado, o emplear el valor lógico de una serie de comparaciones. Paraello ya no operamos de forma binaria, sino que obtenemos resultadosbinarios únicos como true o false . Podríamos 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 lógicas que queramos demodo que a nivel de comparación podamos obtener un resultado

Page 51: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 51/189

Expresiones, Estructurasy Excepciones

35

lógico (verdadero o falso). En la siguiente sección se mencionan todoslos operadores de comparación que se pueden emplear para realizarcomparaciones entre cadenas, números, tuplas, listas y/o registros.

Nota

Además de los operadores and y or , en Erlang existen otros comoandalso y orelse . El resultado a nivel de cálculo es el mismo. Loúnico que varía es que los primeros realizan una comprobaciónabsoluta de los valores pasados, evaluando y comparando todoslos valores, mientras que los presentados recientemente, realizanuna comprobación vaga.

Esto quiere decir que se evalúa la primera parte de la expresióny, en caso de andalso (por ejemplo), si es falsa, ya se sabe que

el resultado general será falso, por lo que no se comprueba lasegunda parte, retornando inmediatamente el valor  false . Sonútiles si la comprobación se debe hacer consultado una funciónque tiene un coste de comprobación asociado, ya que muchasveces es mejor ahorrarse esas ejecuciones. Lo mismo se aplica auna comprobación que pueda fallar por lo que necesitamos otraanterior que descarta la segunda. Por ejemplo:

is_list(List) andalso length(List)

Si List no fuese una lista, la ejecución de length/1 fallaría. Al

emplear andalso esto no sucede, ya que sólo se comprueba laprimera parte, y al obtener false finaliza las comprobaciones.

1.3. Precedencia de Operadores

El orden de los operadores para Erlang de más prioritario a menosprioritario es el siguiente:

Operador Descripción

: Ejecución de funciones# Resolución de registros

+ - bnot not Unitarios

/ * div rem band and División, Multiplicación e Y lógico.

+ - bor bxor bsl bsr or xor Suma, resta y O inclusivo yexclusivo.

++ -- Agrega/Sustrae de conjuntos/listas.

== /= =< < >= > =:= =/= Comparaciones

Page 52: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 52/189

Expresiones, Estructurasy Excepciones

36

Operador Descripción

andalso Y lógico con comprobación vaga

orelse O lógico con comprobación vaga= ! Asignación y Paso de mensaje

catch Captura de errores

2. Estructuras de ControlA diferencia de los lenguajes imperativos en Erlang sólo hay dosestructuras de control: if  y case ; aunque se puedan parecer a lasestructuras que existen en otros lenguajes, difieren.

Estas estructuras se basan en la concordancia de sus expresiones. Ambastienen que realizar una concordancia positiva con una expresión yejecutar un código que retorne un valor.

Como el que encajen los valores es tan importante para estas estructuras,y para la mayoría de estructuras en ejecución dentro de la programaciónde Erlang, en general, dedicaremos una parte a estudiar lo quellamaremos a partir de ahora como concordancia  y seguidamenteveremos las estructuras donde se aplica.

2.1. ConcordanciaEn este apartado revisaremos un aspecto bastante importante en lo querespecta a la programación en Erlang y que conviene tener interiorizado,lo que facilitará mucho la programación en este lenguaje. Me refiero a laconcordancia (en inglés match). Podríamos definir esta expresión como lacualidad de una estructura de datos de asemejarse a otra, incluso aunquehaya que aplicar asignación para ello.

Si tenemos un conjunto de datos, por ejemplo una lista, podemos hacerun simple concordancia haciendo:

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

Si realizamos esta asignación, veremos que nos da como resultado[1,2,3], es decir, se acepta que el valor de la izquierda es igual al de laderecha (como en matemáticas: es un aserto válido).

Ahora bien, si tenemos el dato de la derecha que lo desconocemos, comohabíamos visto en la listas, podemos hacer:

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

Page 53: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 53/189

Expresiones, Estructurasy Excepciones

37

Esto nos dará como resultado la asociación a A, B y C de los valores 1, 2 y3, respectivamente, por lo que retornará como en el caso anterior, [1,2,3].

En la sección de listas comentamos más formas de hacer concordancia através de la agregación de conjunto (++) o con la lista en formato cabeza- cola ([H|T]). Con respecto a las tuplas, esto no es aplicable, ya que la tuplatiene valores fijos, pero podemos ignorar los que no nos interesen de lasiguiente forma:

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

Con el símbolo de subrayado (o guión bajo "_"), le decimos al sistemaque en ese espacio debe de haber un dato (del tipo que sea: lista, tupla,átomo, número o registro), pero que no nos interesa.

2.2. Estructura case

La primera estructura de control que vamos a tratar, probablemente lamás usada, es case . Esta estructura toma un valor inicial como referenciay busca entre las opciones que se especifican la primera que concuerdepara ejecutar su bloque funcional y retornar el valor que establezca laelección.

Como dijimos en un principio, la denominación de funcional, implica quecada acción, estructura y función debe retornar un valor. Las estructurasde control como case no son una excepción.

Veamos un ejemplo:

> Impuesto = case irpf ofirpf -> 0.25;iva -> 0.18;_ -> 0

end.0.25

En este ejemplo podemos ver cómo, si la estructura que se indica encase casa con cualquiera que se suceda en las subsiguientes líneas, seejecuta un bloque concreto, retornando el resultado de la ejecución dedicho bloque (en este ejemplo sólo un valor). Si no se encontrase ningúnvalor que casara, la estructura no podría retornar nada y daría un error.Es aconsejable acabar con un subrayado (_) que casa con todo y tomarlocomo valor por defecto, a menos que se quiera expresamente que falleen caso de que no se contenga un valor apropiado.

Podemos ver otro ejemplo más complejo como el siguiente:

> Resultado = case Fecha of

Page 54: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 54/189

Expresiones, Estructurasy Excepciones

38

{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 función date() el sistemaentenderá que casa con el primer bloque, ya que es una tupla de 3elementos, convertirá cada dato y lo concatenará con los guiones pararetornarlo en modo texto con formato A-M-D. Si lo que enviamos es un

texto en una lista binaria separado por barras inclinadas (/), tomará cadaparte y lo representará análogamente. En caso de no casar con ningunode los anteriores, retorna una cadena vacía.

La estructura case  puede agregar condicionales a cada opción para laconcordancia. Esto es lo que se conoce como guardas1. Estas expresionesse pueden agregar empleando conexiones como: andalso o "," y orelse o";". Estas guardas se agregan tras cada opción 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 parsearán dentro decada bloque son del tipo que se esperan, y que algo como una tupla quecontenga listas de caracteres no haga fallar el primer bloque de opción.

Para las guardas se pueden emplear tanto "," como and , o andalso, encaso de que se quiera el comportamiento del y lógico; o ";", or o orelse ,para conseguir el comportamiento del o inclusivo lógico.

La diferencia existente entre las tres formas es que el agregado also oelse hace que sea una comprobación vaga pudiendo finalizar antes de

1Esta expresión inglesa se ha traducido en sitios como aprendiendo erlang como guardas .

Page 55: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 55/189

Expresiones, Estructurasy Excepciones

39

evaluar todos los predicados. Los signos de puntuación se comportan dela misma forma en este caso.

La diferencia entre los signos "," y ";" con andalso y orelse  es quelos signos capturan excepciones. Es decir mediante el uso de lossignos de puntuación se ignorarán los fallos que puedan suceder en laevaluación, continuando con la evaluación de lo siguiente. Para aclararmejor las diferencias veamos tres ejemplos de código similares pero quefuncionan 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 código directamente, ya que estamossumando 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 todaesa comprobación por ser errónea, pasando a comprobar el siguientebloque y devolviendo fail. Por último, con el signo ";", en lugar de tomarese resultado como no válido e invalidar toda la comprobación como elcaso anterior, sólo da como inválida la primera parte y pasa a comprobarel 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 . Estaestructura guarda cierta similitud con las que se emplean en loslenguajes imperativos, salvo porque debe existir una opción de códigoque sea ejecutable en caso de que la cláusula previa se cumpla; ademásy en todo caso que se debe retornar siempre un valor.

Si nos fijamos bien esta estructura podría tomarse como unasimplificación de la estructura case  anterior. La única diferencia radicaen la eliminación de los bloques de concordancia. Es decir, sólo emplealas guardas.

Por ejemplo, la siguiente estructura if devuelve el caso1 si el día de hoyestá entre los valores 1 y 10, y si es sobre 11 y 20, caso2. En caso de

Page 56: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 56/189

Expresiones, Estructurasy Excepciones

40

ejecutarse la función mostrada con los valores mayores o iguales a 21daría 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 ifexpression

Este error es debido a que esta estructura, al igual que el resto deestructuras existentes en Erlang, debe de retornar un valor y en caso deno poder ejecutar ningún bloque de código para resolver la función o

valor que debe devolver, origina el fallo.

Importante

En otros lenguajes, el operador de mayor que  (>) y menor que (<) se sitúa siempre antes del signo igual, mientras que, como sevio en la tabla de precedencia de operadores, según si es uno uotro, se coloca de modo que apunte siempre hacia el símbolo deigualdad.

En la misma tabla de precedencia de operadores se puede ver queand y or tienen más 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 comparación entreparéntesis.

Para que el sistema no nos falle cuando introduzcamos fechas a partirdel día 21, vamos a definir una acción 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 comodín no se hace sobreuna variable que pueda contener cualquier valor (como en el caso desubrayado, por ejemplo), sino se emplea la palabra reservada true portratarse de predicados lógicos.

2.4. Listas de Comprensión

Una de las ventajas de la programación funcional es sin duda su caracterdeclarativo. El hecho de poder tener una estructura como las listas decomprensión, nos puede ayudar a extraer información sin problemas,

Page 57: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 57/189

Expresiones, Estructurasy Excepciones

41

indicando: de donde procede esta información, cuál queremos que sea suformato de salida y las condiciones que debe de cumplir nos proporcionadicha información al instante.

Por ejemplo, si queremos sacar de una lista sólo los números pares, seríatan sencillo como:

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

Si expresamos esto mismo en lenguaje natural sería algo así como:[ Dame X || Donde X es un elemento de la lista <- [1,2,3,4,5], tal que la condición X rem 2 =:= 0 se cumpla .

Las listas de comprensión tienen tres partes que se enmarcan dentro delos corchetes. La primera es la proyección de los elementos, es decir,indica la forma en la que se presentarán los datos o en la que queremosque se configure la salida de la ejecución de la lista de comprensión.

La segunda es la selección de los datos. Esta parte está separada de laprimera por dos pipes (||) y tiene una flecha de derecha a izquierda queindica a la derecha el origen de los datos y a la izquierda el patrón o formade los datos.

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

condiciones de la selección. Las condiciones que debe de cumplir cadaelemento de la lista, para ser seleccionado. En el caso del ejemplo seindicó que el valor de X debía de ser par (que su remanente fuese ceroen una división por dos).

Nota

Las listas de comprensión son uno de los elementos másimportantes del lenguaje, por lo que conviene que se tengamuy presente su forma, la utilidad que tienen con respecto a laselección y proyección de información y realizar pruebas hastacomprender su funcionamiento completa y correctamente.

Un truco bastante útil que yo empleo es compararlo con unasentencia SELECT de SQL, ya que tiene la parte de la proyección(inmediatamente después de SELECT), la parte de la selección (laparte del FROM) y las condiciones de la selección para cada tupla(la parte del WHERE).

Un ejemplo más completo, teniendo listas de listas, pero siendo unamatriz fija de 2xN, por ejemplo, podemos realizar la siguiente selección:

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

Page 58: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 58/189

Expresiones, Estructurasy Excepciones

42

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

La lista resultado nos muestra, dentro de una sublista de dos elementosa los que asociamos como (Y,X), el hecho de que el elemento Y deba deser par y el elemento X mayor o igual a 4. Por lo que, en esta definición,concuerdan los números 4 y 6.

3. ExcepcionesErlang es tolerante a fallos. Esto le viene dado por el empleo de procesosen lugar de hilos. Si un proceso muere y deja su estado de memoriacorrupto no afectará a otros procesos, ya que ni siquiera compartenmemoria (cada proceso tiene la suya propia y es otra de las propiedades

de Erlang el nada compartido o share nothing en inglés), ni la ejecuciónde uno está condicionada o afecta a otros procesos.

El tema de los procesos lo veremos en el siguiente capítulo de formamás extensa. Ahora vamos a centrarnos en las excepciones, porque,¿qué suecede cuando un proceso encuentra un fallo o una situacióninesperada por el programador? Normalmente se dispara una excepciónque hace que el proceso muera.

En el siguiente capítulo veremos que eso en muchos casos es asumible eincluso deseable. Pero también hay casos en los que, si el código maneja

recursos que hay que tratar de llevar a una situación segura antes de quesuceda lo inevitable, es preferible intentar de realizar algún tratamientopara esa excepción.

3.1. Recoger excepciones: catch

El primer tipo de instrucción que se introdujo en Erlang para la capturade errores y excepciones es catch. Este comando se puede anteponera la ejecución de una función o de cualquier instrucción. Si se genera unerror, catch permite transformarlo en un dato recibido por la funcióno instrucción que se hubiese ejecutado. Veamos un pequeño 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 ejecución de la primera expresión nos lleva a una excepciónque propocaría la finalización de ejecución del proceso, mientrasque anteponiendo catch a la misma expresión, Erlang convierte esaexcepción en un tipo de dato que se podría procesar a través de unaestructura de control.

Page 59: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 59/189

Expresiones, Estructurasy Excepciones

43

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 elcasoError , que debe de ser manejado por el código que toma el retornode esta instrucción.

Importante

En este caso es una mala idea haber capturado la excepción yaque tapa un error de código que hemos provocado y que, graciasa catch, hace que consideremos el código como correcto, cuandono es así.

3.2. Lanzar una excepción

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

> 2+3=5.5

Si empleamos variables para almacenar los valores, y cometemos unerror:

> A=2, B=3, 5=A+A.** exception error: no match of right hand side value 4

Como el código es erróneo y 5 no es igual a 4, el sistema se detiene

en ese punto. Esto nos garantiza que, si el código es crítico y no debede contener errores, en unas pruebas podría aparecer el error y sersolucionado.

Además de esta técnica, podemos lanzar excepciones con mensajesde error concretos, por si quisiéramos a otro nivel capturarlos paraprocesarlos. Estos se lanzarían a través de throw . Podemos verlo másclaro a través de un ejemplo:

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

Page 60: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 60/189

Expresiones, Estructurasy Excepciones

44

En caso de que quisiéramos capturarlo con catch, el sistema trata estelanzamiento de excepción como un error real provocado por el usuario,por lo que se podría 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, más clara y potenteque catch. Este bloque se presenta como los que existen en los lenguajesimperativos. La parte try  da cabida a ejecución de código que seráobservado por la estructura y en caso del lanzamiento de cualquierexcepción, ya sea por fallo, throw o porque se haya ordenado al procesoacabar su ejecución, 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 sedetallan con su clase, que puede ser cualquiera de las tres: throw, exito error. A continuación y después de los dos puntos (:) está la variableque contendrá el mensaje en sí del error para poder emplearlo dentrodel bloque de código de recuperación.

Esta sentencia presenta también una zona en la que poder ejecutaracciones que se lleven a cabo tanto si el código falla como si no. Estasección recibe el nombre de after , y es un bloque de código que se agregatras catch. Por ejemplo, si queremos imprimir por pantalla un saludo falleo no el código:

> try

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

El código se ejecuta de modo que, como after está dentro de la estructura,hasta que esa sección no termina (en este caso imprimir Adios  porpantalla) la estructura no retorna el valor correspondiente a su ejecución(la excepción a través de la rama error:Error ).

Page 61: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 61/189

Expresiones, Estructurasy Excepciones

45

Nota

Podríamos profundizar más en estas estructuras, pero lo dejo

en este punto porque me gusta más la filosofía de Erlang: let it crash (deja que falle); que indica que el sistema debe depoder fallar para volver a iniciar su ejecución de forma normal,ya que mantenerse en ejecución tras un fallo podría provocaruna situación imprevista que, además, se prolongase, con lo quedificultaría aún más la detección del fallo.

3.4. Errores de ejecución más comunes

En esta sección daremos un repaso a los errores de ejecución máscomunes que suelen surgir en Erlang cuando programamos de forma que

el lector pueda corregirlos rápidamente.

function_clause

Cuando se llama a una función con parámetros incorrectos, ya seaen número, concordancia o por guardas, se dispara esta excepción:

> io:format("hola", [], []).** exception error: no function clause matching...

case_clause

Prácticamente igual la anterior. Este se dispara cuando no hayconcordancia con ningún bloque (y sus guardas, en caso de quetuviese), dentro de la cláusula 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 nohay ninguna guarda del if aplicable. El sistema indicará que no hay

rama true disponible, ya que es una práctica habitual el disponerde la misma.

> if false -> "" end.** exception error: no true branch found when eval...

badmatch

Suelen suceder cuando falla la concordancia (matching ), ya sea alintentar asignar una estructura de datos sobre otra que no tiene lamisma forma o cuando se intenta hacer una asignación sobre unavariable que ya tiene un valor.

Page 62: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 62/189

Expresiones, Estructurasy Excepciones

46

> A=1, A=2.** exception error: no match of right hand side value 2

badarg

Se suele disparar cuando llamamos a una función con argumentoserróneos. A diferencia de las ya vistas esta excepción es introducidacomo una validación de argumentos por el programador fuera de lasguardas, por lo que para emplearla, debemos de crear un bloque ennuestras funciones de validación de argumentos que, en caso de noser correctos, la lancen. Un ejemplo de función que dispone de esto:

> io:format({hola}).** exception error: bad argument

undef 

Lanzada cuando se llama a una función que no está definida (noexiste), ya sea por su número de parámetros o por su nombre dentrodel módulo:

> lists:no_existe().** exception error: undefined function lists:no_existe/0

badarith

Esta excepción es para errores matemáticos (aritméticos). Sucedecuando se intenta realizar una operación con valores incorrectos(como una suma de un número 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 contieneuna función. Un ejemplo:

> A = hola, A(12).** exception error: bad function hola

badarity

Es un caso específico de badfun, en este caso el error es debido aque a la función que contiene la variable, se le pasa un número deargumentos que no puede manejar, porque son más o menos de losque soporta. Un ejemplo:

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

Page 63: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 63/189

Expresiones, Estructurasy Excepciones

47

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

system_limit

Se alcanzó el límite del sistema. Esto puede pasar cuando:tenemos demasiados procesos limitados por el parámetro deprocesos máximos (se puede ampliar), o demasiados argumentosen una función, átomos demasiado grandes o demasiados átomos,demasiados nodos conectados, etc. Para una mejor optimizacióndel sistema y entendimiento del mismo podemos leer la Guía deEficiencia de Erlang (en inglés)2.

Importante

Hay que tener especial cuidado con los errores de system_limit .Son lo suficientemente graves como para parar todo el sistema (lamáquina virtual de Erlang al completo).

Si capturamos estos errores, se presentarán de la forma:

{Error, Reason}

Donde Error  puede tomar cualquiera de los valores indicadosanteriormente (bararg, function_clause, cause_clause, ...) y Reason tendráuna descripción de las funciones que fueron llamadas, para llegar a ese

punto.

Nota

A partir de la versión de Erlang R15, en Reason se puede verademás el nombre del fichero y número de línea en el se realizóla llamada, lo cual facilita la detección de errores.

2 http://www.erlang.org/doc/efficiency_guide/advanced.html

Page 64: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 64/189

48

Capítulo 4. Las funciones y

módulosDivide y vencerás.—Refrán popular 

Hasta el momento hemos estado ejecutando el código desde la consola.Todas las pruebas y códigos de ejemplo vistos se han escrito pensandoen que serán ejecutados desde la consola de la máquina virtual de Erlang.Normalmente la programación en Erlang no se produce en la consola,sino que se realiza a través de la escritura de módulos en los que hayfunciones.

Las funciones se podrían tratar como otras estructuras de control (comocase o if ), ya que disponen de elementos similares aunque son elementosde definición. No se ejecutan en el momento como las estructuras decontrol, sino que la ejecución se realiza mediante una llamada a lafunción.

En esta sección revisaremos los conceptos de módulo, función, elpolimorfismo y otros aspectos más avanzados de funciones y módulosque permite Erlang.

1. Organización del códigoEl código en Erlang se organiza en módulos y dentro de cada módulopuedes encontrar funciones. Anteriormente ya hemos visto algunosde estos módulos, como el caso de proplists , por ejemplo, en el queempleábamos el uso de funciones como get_value .

Un módulo se define en un fichero a través de unas instruccionesde preprocesador iniciales que nos permiten definir el nombre ylas funciones que queremos exportar (para emplear desde fuera del

módulo).

El código podría ser como sigue:

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

mi_funcion() ->"Hola mundo!".

El módulo del código anterior llamado mi_modulo debe guardarse en unfichero con el nombre mi_modulo.erl. El módulo exporta, o pone a

Page 65: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 65/189

Las funciones y módulos

49

disposición de otros módulos y de la consola la posibilidad de usar lafunción mi_funcion, cuya aridad (o número de parámetros) es cero.

Para simplificar el tema de la exportación en la codificación de nuestrosprimeros módulos hasta que nos acostumbremos a ella, podemos obviarel hecho de que habrá funciones privadas para el módulo y dejarlas todasabiertas. Esto se haría 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 demodo que no haya que nombrarlas una a una en la sentencia export .

Nota

Una vez tengamos el fichero creado, compilarlo es tan sencillocomo ir a una consola del sistema operativo y ejecutar:

erlc mi_modulo.erl

Esto genera un fichero mi_modulo.beam que será el queempleará la máquina virtual para acceder a las funciones creadas.

También es posible compilar un módulo en la consola de Erlang,en nuestro ejemplo, escribiendo:

> c(mi_modulo)

Lo cual compilará el código creando el fichero mencionadoanteriormente, dejándolo disponible para su uso.

Desde la consola de la máquina virtual podemos ejecutar:

> mi_modulo:mi_funcion().

"Hola mundo!"

La máquina virtual de Erlang busca el fichero beam en su ruta de módulospor defecto y luego en el directorio actual. Si lo encuentra, lo carga ybusca la función dentro del mismo. En caso de que no encontrase lafunción retornaría un fallo.

Page 66: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 66/189

Las funciones y módulos

50

Importante

A diferencia de otros lenguajes donde los paquetes, móduloso librerías se pueden encontrar de modo jerárquico, Erlangestablece el nombre de sus módulos de forma plana. Esto quieredecir que si existe un módulo llamado mi_modulo e intentamoscargar otro módulo con el mismo nombre, se emplearía el quetuviese la fecha de compilación más reciente.

Hay que tener cuidado con el nombre de los módulos. Porejemplo, si se creara un módulo vacío de nombre erlang  y seintentara cargar el sistema completo se detendría, ya que seintentarían emplear las funciones del propio sistema Erlang,esenciales para su funcionamiento, y no estarían presentes eneste nuevo módulo de fecha más reciente.

2. Ámbito de las funciones

Cuando creamos un módulo podemos importar y exportar funcionesdentro o hacia fuera de él. El módulo encapsula un conjunto de funcionesque pueden ser accesibles por otros módulos si se especifica suexportación.

La declaración export es una lista que puede contener tantas referenciasde funciones como se deseen publicar, e incluso pueden existir varias

declaraciones diferentes de export dentro de un mismo módulo.

La declaración import , al igual que la anterior, contiene una lista defunciones como segundo parámetro, que se importan desde el móduloque se detalla como primer parámetro. Puede haber tantas declaracionescomo se necesiten dentro de un módulo y cada declaración es sólo parala importación desde un módulo.

Por ejemplo, tenemos el código de este módulo:

-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 exportación, estamos dandoexclusivamente acceso a la función get con un parámetro, tanto a otrosmódulos que importasen traductor como a la consola. Desde el punto de

vista de la importación, tenemos disponible la funciónget_value

del

Page 67: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 67/189

Las funciones y módulos

51

módulo proplists de modo que no tengamos que llamarla de forma fully qualified 1.

Nota

La importación es una técnica que puede hacer confuso elcódigo escrito. Se recomienda no emplearla a menos que el usomasificado de la función en cuestión sea más beneficioso para lalectura del código que invocarla de manera fully qualified .

3. Polimorfismo y ConcordanciaUna de las particularidades de las funciones de Erlang, es que disponende polimorfismo. Si tuviésemos que programar una función que tuviesealgunos de sus parámetros con valores por defecto, podríamos emplearel polimorfismo tal y como se da en muchos otros lenguajes imperativos,definiendo dos funciones con distinto número de parámetros, de lasiguiente forma:

multiplica(X, Y) ->X * Y.

multiplica(X, Y, Z) ->X * Y * Z.

En este caso, vemos que si la función es llamada con dos parámetros, seejecutaría la primera forma, ya que casa con el número de parámetros, yen cambio, si pasamos tres parámetros, se ejecutaría la segunda forma.

En Erlang sin embargo este concepto se puede completar agregando lacaracterística de la simple asignación y la concordancia, de modo quenos 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 función anterior nos retorna un área, dependiendo del númerode argumentos pero además del contenido del primer parámetro.Gracias a ello, podemos tener funciones con el mismo número deparámetros y diferente comportamiento. Como se puede observar, el

1Fully Qualified , deriviado de su uso en los nombres DNS como FQDN, reseña la llamada a una funciónempleando toda la ruta completa para poder localizarlo, es decir, empleando también el módulo.

Page 68: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 68/189

Las funciones y módulos

52

primer parámetro puede contener los valores: cuadrado, rectangulo,triangulo o circulo (sin acentuar, ya que son átomos). En caso derecibir, por ejemplo cubo, el sistema lanzaría una excepción al no poder

satisfacer la ejecución solicitada.

Importante

Cuando se emplea el polimorfismo, es decir la declaración deun mismo nombre de función para igual número de parámetrospero diferente contenido, se debe de separar la definición de unafunción de la siguiente a través del punto y coma (;), mientrasque la última definición debe de llevar el punto final. Esto esasí para que los bloques de funciones polimórficas de este tipoestén siempre agrupados, conformando una única estructura máslegible.

4. GuardasAnteriormente ya vimos las guardas en las estructuras de control case e if . Como la estructura de función es tan similar a las estructuras decontrol, también contempla el uso de guardas, lo que le permite realizarun polimorfismo todavía más completo.

Por ejemplo, si queremos, del ejemplo anterior del cálculo de áreas,asegurarnos de que los datos de entrada son numéricos, podríamosreescribir el código 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 más de validación, asegurándonos de quelas entradas de las variables sean numéricas o en caso contrario que nose ejecutaría esa función. Podríamos agregar en las condiciones que laBase sea mayor de 0, al igual que la Altura y Radio, y cualesquiera otrascomprobaciones más que se nos puedieran ocurrir.

5. ClausurasSi revisamos un momento la teoría lo que ahora vamos a ver podríaencajar perfectamente como clausura, lambda o función anónima. Enprincipio, las definiciones:

Page 69: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 69/189

Las funciones y módulos

53

Se llama clausura (en inglés clousure ) a una función junto a un entornoreferenciado de variables no locales. Esto quiere decir que la funcióntiene acceso a las variables del entorno en el que es definida como

si fuesen globales. Por ejemplo, si definimos una función calculadora dentro de otra función llamada  factoria , si en esta última función haydefinida una variable llamada contador , esta variable será accesibletambién por calculadora .

Por otro lado, tenemos el cálculo lambda, inventado por Alonzo Churchy Stephen Kleen en 1930, que en un entorno matemático define loque es una función para abstraer las ecuaciones en un lenguaje mássimplificado (Peter Landin se encargó de llevar esta teoría a Algol 60). Elcaso es que la teoría de funciones, subprogramas y subrutinas se basaen esta teoría, pero el nombre lambda, en lenguajes imperativos ha sido

otorgado a funciones anónimas.

Por último, las funciones anónimas no son más que funciones que nose declaran con un nombre sino que son declaradas y almacenadas enuna variable, de modo que la variable es empleada para hacer llamadas aotras funciones, pudiendo ser pasada como parámetro o retornada comoresultado, ya que en sí, es tratada como un dato.

Las clausuras de Erlang se basan en todas estas premisas. Son funcionesque, al definirse, pueden tomar el valor de las variables del entorno en elque son definidas (ya que las variables son de simple asignación y toman

su valor en ese momento), que cumplen con la adaptación del cálculolambda de Church y Kleen y son anónimas puesto que su definiciónes como una instanciación que se almacena en una variable y puedeser enviada como parámetro, retornada como valor y además de esto,empleada como una función.

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 definición de la clausura,introduciendo dentro de su contexto el uso de una variable del entornoen el que está siendo definida, en este caso la variable A. De estemodo al ejecutar la función F, multiplica la variable que se le pasa comoparámetro por la que tiene contenida.

Podemos hacer también que una función normal, o incluso una anónima,nos retorne una función específica que haga una acción concreta segúnlos datos con los que haya sido llamada la primera:

Page 70: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 70/189

Las funciones y módulos

54

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

multiplicador(X) when is_integer(X) ->fun(Y) -> X * Y end.

Emplearíamos este código 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 sólo se permite generar una clausura dentrode otra, sino que la generación de las clausuras puede tener tambiénguardas. Si quisiéramos agregar una clausura más al código, para truncarel valor de un número en coma flotante en caso de que llegase como X,podríamos 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 lamisma forma, tanto si se envía un dato de tipo entero como si el dato esde tipo real (o en coma flotante).

Nota

Referenciar una función definida de forma normal como una

función anónima o clausura se consigue de la siguiente forma:

F = fun io:format/1.

Esta declaración nos permitiría uilizar format/1 como unaclausura más empleando directamente F. Esto viene muy bienpara cuando se tienen varias funciones para trabajar de una ciertaforma y se desea pasar la función elegida como parámetro a uncódigo donde se empleará.

Por último, voy a comentar el uso de las clausuras en la evaluación

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

Page 71: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 71/189

Las funciones y módulos

55

trabajar con una lista de infinitos términos, o incluso con un contenidoque no queremos que esté siempre presente, sino que se vayagenerando 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, podríamos 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 bucleinfinito), gracias a la evaluación perezosa de Erlang cada número seva generando a medida que vamos avanzando. Retomaremos este usocuando tratemos el tema de la recursividad.

6. Programación FuncionalCuando se piensa en programación funcional, normalmente, se piensaen las listas de comprensión y en funciones sobre listas como son map, filter o fold .

Estas funciones realizan un tratamiento de datos como podría hacerloun bucle en los lenguajes imperativos. En realidad termina siendo máspotente ya que, debido a su naturaleza, se puede paralelizar.

A través del uso de clausuras, podemos hacer que se aplique un códigoespecífico a cada elemento de una lista de elementos. Veamos la lista defunciones más importantes de este tipo que provee Erlang:

map/2

Se ejecuta la clausura pasada como parámetro, recibiendo cadaelemento de la lista como parámetro y retornando un valor por cadallamada que será almacenado y retornado por map/2 al final de laejecución de todos los elementos. Por ejemplo:

> L = [1,2,3,4].

Page 72: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 72/189

Las funciones y módulos

56

> lists:map(fun(X) -> X * 2 end, L).[2,4,6,8]

any/2Se evalúa cada elemento con la clausura pasada como parámetro,debiendo retornar ésta true  o  false . Si alguno de los elementosretorna true , la función any/2 retorna también 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 elementosevaluados deben retornar true . En el momento en el que unoretorne false , la función all/2 retornaría 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 ejecución de la clausura a cada elemento de la lista. Enprincipio es igual que map/2, salvo que foreach/2 no guarda elretorno 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).1234ok

foldl/3 - foldr/3

Esta función se encarga de ejecutar la clausura pasando comoparámetro el elemento de la lista y el retorno de la ejecuciónanterior. Es como si encadenase la ejecución de las clausuras, queforzosamente deben aceptar los dos parámetros. La última letra

Page 73: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 73/189

Las funciones y módulos

57

(l o r ) indica desde donde se inicia la toma de elementos de lalista. Left o izquierda sería desde la cabeza hasta la cola, y right oderecha empezaría a tomar elementos por el final de la lista hasta

el principio. A la función se le pasan tres parámetros, el primero esla 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 combinación de map/2 y fold/3.Encadenan los resultados de cada una de las clausuras de laanterior a la siguiente comenzando por un valor inicial, guardandoel resultado de ejecución de cada clausura. El retorno de la funciónclausura debe ser una tupla en la que el primer valor es el resultadode la parte map/2 y el segundo valor es el retorno para seguirencadenando. El retorno de ambas funciones es también una tuplaen la que el primer elemento es una lista con todos los elementos(tal y como lo haría map/2) y el segundo valor es el resultado de laparte 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 cadaelemento. La clausura debe retornar verdadero o falso (true o false ).Cada elemento que cumpla con la clausura será agregado a la listadel 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 confilter/2, pero en el momento en el que un valor retorna falsotermina la ejecución. Por ejemplo:

> L = [1,2,3,4],> F = fun(X) -> if X =< 2 -> true; true -> false end end,> lists:takewhile(F, L).

Page 74: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 74/189

Page 75: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 75/189

Las funciones y módulos

59

Nota

Erlang implementa un sistema denominado tail recursion (o

recursividad de cola), que hace que la pila de una llamada ala siguiente se libere dado que el código para ejecutar en esafunción ya no es necesario. Esto evita que se produzcan errorespor desbordamiento de pila, convirtiendo el código recursivo eniterativo, al menos a efectos de consumo de memoria.

El ejemplo más simple de recursividad es la operación de factorial:

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

fact(0) -> 1;

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

En esta functión, tenemos dos casos diferenciados. El caso particularrepresentado por la primera declaración de función, porque sabemosque el factorial de cero es uno. También disponemos del caso general,que serían el resto de casos para una variable X lo que se resuelvenmultiplicando cada valor por su anterior hasta llegar a cero.

Un tipo de algoritmos que se puede implementar muy fácilmente conrecursión son los de divide y vencerás. Estos algoritmos se basan en ladivisión del problema en subproblemas más pequeños pero similaresllegando a los casos particulares. Se resuelve cada pequeño problemade forma aislada y después se combinan las soluciones (si es necesario),para conseguir la solución global del problema.

Las tres partes que se pueden diferenciar en este algoritmo son:separación, recursión y combinación. Podemos ver algunos algoritmosclásicos como los de ordenación de listas que nos pueden ayudar acomprender mejor cómo funciona la recursividad.

7.1. Ordenación por mezcla (mergesort )

Comenzaremos viendo el algoritmo de ordenación por mezcla  (omergesort ), que se basa en hacer una partición de los elementos simple,una recursividad sobre cada parte para descomponer el problema lo másque se pueda y una mezcla en la que se va realizando combinación delas partes ordenadas. Este algoritmo es simple en las dos primeras partesy deja la complejidad para la tercera. Primero partimos la lista en trozosde tamaño 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]}

Page 76: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 76/189

Las funciones y módulos

60

Esto lo podemos dejar dentro de una función que se llame separa/1

para semantizar el código y diferenciarla dentro del algoritmo. La mezclapodemos hacerla a través de recursión también, de modo que, dadas dos

listas ordenadas podríamos 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 cabecerade las listas, el que cumpla con la condición indicada (el que sea menor),concatenando el elemento y llamando a la función con los elementosrestantes. Para que este algoritmo funcione ambas listas deben de estarordenadas, por lo que hay que ir separando elementos hasta llegaral caso particular, que será la comparación de un elemento con otroelemento (uno con uno). Para conseguir esto, realizamos la siguienterecursión:

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 función ordena/1, con lo que llega hasta lacomparación de un sólo elemento con otro. Después un nivel más alto dedos con dos, tres con tres, y así hasta poder comparar la mitad de la listacon la otra mitad para acabar con la ordenación de la lista de números.

Como dijimos al principio, la complejidad se presenta en la combinación,o función mezcla/2, que de forma recursiva se encarga de compararlos elementos de una lista con la otra para conformar una sola en la queestén todos ordenados.

El código completo del algoritmo es el siguiente:

-module(mergesort).-export([ordena/1]).

separa(L) ->lists:split(length(L) div 2, L).

Page 77: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 77/189

Las funciones y módulos

61

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 función ordena/1, de modo quepara poder emplear el algoritmo habría que hacerlo así:

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

7.2. Ordenación rápida (quicksort )

En este ejemplo, vamos a llevarnos la complejidad de la parte decombinación a la parte de separación. Esta función, que se llama

quicksort  por lo rápida que es ordenando elementos, se basa en laordenación primaria de las listas para que la mezcla sea trivial.

Este algoritmo se basa en coger un elemento de la lista como pivote yseparar la lista en dos sublistas, una con los elementos menores al pivote(la primera) y la otra con los elementos mayores (la segunda), para volvera llamar al algoritmo para cada sublista.

Esta parte de código la simplificaremos empleando listas decomprensión, 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 recibirán listas ya ordenadascomo parámetros. La mezcla consiste sólo en concatenar las sublistas yretornar el resultado. La parte de la recursividad, es muy parecida a la demergesort . Viendo el código al completo:

-module(quicksort).

Page 78: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 78/189

Las funciones y módulos

62

-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 vencerás se mantiene.Por un lado separamos la lista en dos sublistas seleccionando unpivote , retornando ambas sublistas y el pivote. Las sublistas se ordenanmediante recursión sobre cada sublista por separado.

La ejecución de este código sería así:

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

8. Funciones Integradas

En Erlang existen funciones que no están escritas en Erlang, sino queel sistema las procesa a bajo nivel y forman parte de la máquinavirtual como instrucciones base que se ejecutan mucho más rápido.Estas funciones construidas en el sistema se albergan bajo el móduloerlang . Normalmente no hace falta referirse al módulo para emplearlas

(a menos que exista ambigüedad). Algunas de ellas ya las hemosvisto: is_integer/1, integer_to_list/1, length/1, e incluso lasoperaciones matemáticas, lógicas y otras. Un ejemplo:

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

Estas funciones reciben el nombre de BIF (en inglés Built-In Functions ).Otros ejemplos de BIFs son el cálculo de MD5 (md5/1), el redondeo denúmeros (round/1) y el cálculo de la fecha (date/0) o la hora (time/0).

Page 79: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 79/189

Las funciones y módulos

63

Nota

Robert Virding, uno de los creadores/fundadores/inventores de

Erlang, comentó en un artículo de su blog2, lo confuso que resultadeterminar qué es un BIF y qué no. Un intento de definirlopor parte de Jonas Barklund y Robert Virding disponible en laespecificación (no indica URL específica el autor en su blog), esque un BIF fue una parte del lenguaje Erlang que no disponía deuna sintaxis concreta o especial, por lo que se mostraba como unallamada a función normal.

2http://rvirding.blogspot.com.es/2009/10/what-are-bifs.html

Page 80: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 80/189

64

Capítulo 5. ProcesosCuando estás en un atasco de tráfico con un

Porsche, todo lo que puedes hacer es consumir más combustible que el resto estando parado. La 

escalabilidad va de construir carreteras más anchas,no coches más rápidos.

—Steve Swartz 

Una de las grandes fortalezas de la plataforma de Erlang es la gestión deprocesos. Los procesos en Erlang son propios de la máquina virtual y encada plataforma tienen las mismas características y se comportan de lamisma forma. En definitiva, no se emplean los mecanismos propios del

sistema operativo para ello sino que es la propia máquina virtual quienprovee los mecanismos para su gestión.

Para comenzar analizaremos la anatomía de un proceso en Erlangpara comprender para lo que es, los mecanismos de comunicación deque dispone y sus características de monitorización y enlazado conotros procesos. Muchas de estas características están presentes en losprocesos nativos de sistemas operativos como Unix o derivados (BSD,Linux, Solaris, ...) y otras se pueden desarrollar sin estar a priori integradasdentro del proceso.

Repasaremos también las ventajas e inconvenientes que tienen losprocesos de Erlang. Su estructura aporta ventajas como la posibilidadde lanzar millones de procesos por nodo, teniendo en cuenta quecada máquina puede ejecutar más de un nodo. También presentainconvenientes como la velocidad de procesamiento frente a losprocesos nativos del sistema operativo.

Por último, el sistema de compartición de información entre procesosprogramados para la concurrencia emplea el paso de mensajes en lugarde emplear mecanismos como la memoria compartida y semáforos, omonitores. Para ello proporciona a cada proceso un buzón y la capacidadde enviar mensajes a otros procesos a través de la sintaxis del propiolenguaje, de una forma simple.

1. Anatomía de un ProcesoUn proceso cualquiera, no sólo los que son propios de Erlang, tieneunas características específicas que lo distingue, por ejemplo, de un hilo.Los procesos son unidades de un programa en ejecución que tienen uncódigo propio y un espacio de datos propio (normalmente llamado heap).

Se podría decir que un proceso cumple los principios del ser vivo, yaque puede nacer (crearse), crecer (ampliando sus recursos asignados),

Page 81: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 81/189

Procesos

65

reproducirse (generar otros procesos) y morir (terminar su ejecución).El planificador de procesos de la máquina virtual de Erlang se encargade dar paso a cada proceso a su debido tiempo y de aprovechar los

recursos propios de la máquina, como son los procesadores disponibles,para intentar paralelizar y optimizar al máximo posible la ejecución delos procesos. Esta sería la vida útil de un proceso.

En Erlang el proceso es además un animal social. Tiene mecanismos quele permiten comunicarse con el resto de procesos y enlazarse a otrosprocesos de forma vital o informativa . En caso de que un proceso muera(ya sea debido a un fallo o porque ya no haya más código que ejecutar),el proceso que está enlazado con él de forma vital muere también,mientras que el que está enlazado de forma informativa es notificado desu muerte.

Para esta comunicación, el proceso dispone de un buzón. En este buzónotros procesos pueden dejar mensajes encolados, de modo que elproceso puede procesar estos mensajes en cualquier momento. El envíode estos mensajes no sólo se puede realizar de forma local, dentro delmismo nodo, sino que también es posible entre distintos nodos queestén interconectados entre sí, ya sea dentro de la misma máquina o enla misma red.

Nota

Cuando se lanza un proceso, en consola podemos ver surepresentación, en forma de cadena, como <X.Y.Z>. Los valoresque se representan en esta forma equivalen a:

• X es el número del nodo, siendo cero el nodo local.

• Y son los primeros 15 bits del número del proceso, un índice ala tabla de procesos.

• Z son los bits 16 a 18 del número del proceso.

El hecho de que los valores Y y Z estén representados como dosvalores aparte, viene de las versiones R9B y anteriores, donde Y

era de 15 bits y Z era un contador de reutilización. ActualmenteY y Z se siguen representando de forma separada para no romperesa compatibilidad.

2. Ventajas e inconvenientesHemos realizado una introducción rápida y esquemática de lo que esun proceso en general y un proceso Erlang, para dar una visión a altonivel del concepto. Como dijimos al principio, los procesos en Erlangno son los del sistema operativo y, por tanto, tienen sus diferencias,sus características especiales y sus ventajas e inconvenientes. En este

Page 82: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 82/189

Procesos

66

apartado concretaremos esas ventajas e inconvenientes para sabermanejarlos y conocer las limitaciones y las potencias que proporcionan.

Desde el principio hemos remarcado siempre que una de las potenciasde Erlang son sus procesos, y es porque me atrevería a decir que esel único lenguaje que dispone de una máquina virtual sobre la quese emplean procesos propios de la máquina virtual y no del sistemaoperativo. Esto confiere las siguientes ventajas:

La limitación de procesos lanzados se amplia.

La mayoría de sistemas operativos que se basan en procesos ohilos limitan su lanzamiento a unos 64 mil aproximadamente. Lamáquina virtual de Erlang gestiona la planificación de los procesos

en ejecución y eleva ese límite a 2 millones

1

.La comunicación entre procesos es más simple y más nutrida.

La programación concurrente se basa la compartición de datos, bienmediante mecanismos como la memoria compartida y el bloqueode la misma a través de semáforos, o bien mediante la existencia desecciones críticas de código que manipulan los datos compartidosa través de monitores. Erlang sin embargo emplea el paso demensajes. Existe un buzón en cada proceso al que se le puedeenviar información (cualquier dato) y el código del proceso puedetrabajar con ese dato de cualquier forma que necesite.

Son procesos y no hilos.

Cada proceso tiene su propia memoria y por tanto no comparte nadacon el resto de procesos. La ventaja principal de tener espaciosde memoria exclusiva es que cuando un proceso falla y deja sumemoria inconsistente, este hecho no afecta al resto de procesosque pueden seguir trabajando con normalidad. Si el proceso vuelvea 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í escompartida) afecte a más de un hilo, e incluso al programa entero.

No obstante, no todo es perfecto y siempre hay inconvenientes en lasventajas que se pintan. Por un lado, el hecho de que la máquina virtual deErlang se encargue de los procesos y del planificador de procesos, tienesu coste. Aunque BEAM está bastante optimizada y el rendimiento de lamáquina se ha ido incrementando en cada versión liberada de Erlang,cualquier lenguaje que emplee directamente los procesos nativos delsistema operativo será más rápido.

1No obstante, por máquina virtual lanzada el límite es algo más bajo por defecto, con el parámetro +Pse puede configurar un número mayor, siendo el valor de procesos máximo por defecto de 32.768, ypudiéndose ajustar este valor de 16 a 134.217.727.

Page 83: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 83/189

Procesos

67

3. Lanzando Procesos

El lanzamiento de los procesos en Erlang se realiza con una construccióndel lenguaje, en concreto una función para facilitar su compresión yuso (ya que es un BIF o función interna) llamado spawn/1. Esta funcióninterna se encarga de lanzar un proceso que ejecute el código pasadocomo parámetro, junto con la configuración para lanzar el proceso. Elretorno a esta llamada es el identificador del proceso lanzado.

La identificación de la función, pasada como parámetro a spawn/1

puede realizarse de varias formas distintas. Se puede emplear unaclausura o indicar, a través de una tripleta de datos (módulo, función yargumentos), la función que se ejecutará.

Las opciones que acepta spawn/1 se refieren sobretodo al nodo Erlangen el que se lanza el proceso y al código para ser ejecutado. La primeraparte la veremos un poco más adelante. Ahora nos centraremos en ellanzamiento del código en el nodo actual.

Por ejemplo, si quisiéramos ejecutar en un proceso separado laimpresión de un dato por pantalla, podríamos ejecutar lo siguiente:

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

Podríamos hacer lo mismo en forma de clausura, obteniendo el mismo

resultado:

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

Si almacenásemos el identificador de proceso llamado comúnmente PID2

en una variable veríamos que el proceso ya no está activo mediante lafunción 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 definición un proceso se mantiene vivo mientrastiene código que ejecutar. Obviamente, la llamada a la funciónformat/1termina en el momento en el que imprime por pantalla el texto que sele pasa como parámetro, por lo tanto, el proceso nuevo finaliza en esemomento.

Si el código se demorase más tiempo en ejecutarse, la funciónis_process_alive/1devolvería un resultado diferente.

2PID, siglas de Process ID o Identificador de Proceso.

Page 84: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 84/189

Procesos

68

4. Bautizando Procesos

Otra de las ventajas disponibles en Erlang sobre los procesos, es poderdarles un nombre. Esto facilita mucho la programación ya que sólonecesitamos conocer el nombre de un proceso para poder acceder a él.No es necesario que tengamos el identficador que se ha generado en unmomento dado para ese proceso.

El registro de los nombres de procesos se realiza a través de otra funcióninterna llamada register/2. Esta función se encarga de realizar laasignación entre el nombre del proceso y el PID para que a partir de esemomento el sistema pueda emplear el nombre como identificador delproceso.

El nombre debe de suministrarse como átomo, y cuando se emplee, debede ser también como átomo. Un ejemplo de esto sería el siguiente:

> Pid = spawn(fun() -> timer:sleep(100000) end).<0.53.0>> register(timer, Pid).true

5. Comunicación entre Procesos

Una vez que sabemos como lanzar procesos y bautizarlos para poderlocalizarlos sin necesidad de conocer su identificador de proceso,veamos cómo establecer una comunicación entre procesos. Esta sería lafaceta social de nuestros procesos.

Para que un proceso pueda recibir un mensaje debe permanecer enescucha . Esto quiere decir que debe de mantenerse en un estadoespecial, en el que se toman los mensajes recibidos en el buzón delproceso o en caso de que esté vacío espera hasta la llegada de un nuevomensaje. El comando que realiza esta labor es receive . Tiene una sintaxis

análoga a case con alguna salvedad. En este ejemplo se puede observarla sintaxis que presenta receive :

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

Si ejecutamos esto en la consola, veremos que se queda bloqueada . Estoocurre porque el proceso está a la espera de recibir un mensaje de otroproceso. La consola de Erlang es también un proceso Erlang en sí, siescribiésemos self/0 obtendríamos su PID.

Page 85: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 85/189

Procesos

69

El envío de un mensaje desde otro proceso se realiza a través de unaconstrucción simple del lenguaje. Vamos a probar con un el siguientecódigo:

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

El símbolo de exclamación se emplea para decirle a Erlang que envíe

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

Nota

Cada proceso en Erlang tiene una cola de mensajes que almacenalos mensajes recibidos durante la vida del proceso, para quecuando se ejecute receive , el mensaje pueda ser desencolado yprocesado.

Para poder realizar una comunicación bidireccional, el envío debe deagregar el PID de quién envía el mensaje. Si queremos como pruebaenviar información 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 código, el proceso hijo creado con spawn/1 se mantiene a laescucha desde el momento de su nacimiento. Cuando recibe una tuplacon la forma {P,M} , imprime el mensaje M por pantalla y envía el mensajeadios al proceso P .

Page 86: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 86/189

Procesos

70

El proceso de la consola es quien se encarga de realizar el envío delprimer mensaje hacia el proceso con identificador Pid  agregando supropio identificador (obtenido mediante la funciónself/0) a la llamada.

A continuación se mantiene a la escucha de la respuesta que le envía elproceso hijo, en este caso adios .

Importante

Las secciones de opción dentro de receive pueden tener tambiénguards . En caso de que el mensaje recibido no concuerde conninguna de las opciones dadas será ignorado y se seguirámanteniendo el proceso en modo de escucha.

Como opción de salida para evitar posibles bloqueos en caso de que

un evento nunca llegue, o nunca concuerde, o si simplemente se quiereescuchar durante un cierto período de tiempo, podemos emplear lasección especial after . En esta sección podemos indicarle al sistema unnúmero de milisegundos a esperar antes de cesar la escucha, pudiendoindicar un código específico en este caso.

Si por ejemplo, en el código anterior, queremos que el proceso quelanzamos se mantenga sólo un segundo en escucha y si no le lleganingún mensaje finalice indicando este hecho, podemos reescribirlo dela 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 más segundos y realizamos el envío del mensaje antes de

que finalice este período, el comportamiento es exactamente igual alanterior. Si dejamos el tiempo pasar, el proceso finalizará su ejecucióninformando por pantalla que el tiempo se ha agotado.

Desarrollado en forma de módulo, para aprovechar la recursividad y queel proceso se mantenga siempre activo, podríamos hacerlo así:

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

escucha() ->receive

{Desde, Mensaje} ->

Page 87: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 87/189

Procesos

71

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 función escucha/0 (del módulo homónimo) se mantiene a la esperade mensajes. Acepta dos tipos de mensajes. Por un lado el que yahabíamos visto antes, una tupla {proceso, mensaje} que recibirá desdeotro proceso que se comunica con éste (se presentará por pantalla). Elotro tipo es un simple mensaje de stop. Cuando se recibe, como ya novolvemos a ejecutar la función de escucha/0, el proceso finaliza suejecución.

Además, cada 5 segundos desde el último mensaje enviado, o desde elúltimo tiempo agotado, o desde el inicio de la ejecución, se imprime elmensaje dime algo! , ejecutando recursivamente la función escucha/0

para seguir con el proceso activo.

El código para utilizar este módulo podría ser algo como:

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

Con este ejemplo queda claro que lanzar un proceso es una actividadtrivial, al igual que el intercambio de mensajes entre procesos. Estaes la base sobre la que se fundamenta una de las aplicacionesmás importantes de Erlang, la solución de problemas en entornosconcurrentes. También es la base de la mayoría de código que se escribe

Page 88: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 88/189

Procesos

72

en este lenguaje. A continuación iremos ampliando y matizando aún máslo visto en este apartado.

6. Procesos EnlazadosOtra de las funcionalidades que proporciona Erlang respecto a losprocesos es la capacidad para enlazarlos funcionalmente. Es posibleestablecer una vinculación o enlace vital entre procesos de modo que sia cualquiera de ellos le sucede algo, el otro es inmediatamente finalizadopor el sistema.

Completando el ejemplo anterior, si el código contuviera un fallo (node compilación, sino de ejecución), el proceso lanzado moriría pero alproceso lanzador no le sucedería absolutamente nada.

El siguiente fragmento de código 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 termineinmediatamente. Al proceso principal, el de la consola, no le sucedeabsolutamente nada. Ni tan siquiera se entera, ya que el proceso fuelanzado sin vinculación.

Nota

La consola está diseñada para procesar las excepciones, por loque una vinculación de error con la misma no provoca su cierrepor el error recibido, sino que simplemente indica que ha recibidouna excepción de salida.

Cambiandospawn/1 por spawn_link/1el lanzamiento del proceso serealiza con vinculación, 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 más completo en un módulo. Tenemos dosprocesos que se mantienen a la escucha por un tiempo limitado y uno deellos en su código tiene un error. En este caso ambos procesos, aunqueindependientes, finalizarán, ya que uno depende del otro (así se indicaal lanzarlos enlazados).

Page 89: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 89/189

Procesos

73

El código sería 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 ofA when A < 5 -> ok

end,zape(A+1).

Al ejecutar la función lanza/0, se genera un nuevo procesoindependiente (sin enlazar). Este proceso a su vez genera otro enlazadoque ejecuta la función zipi/1. Después se mantiene ejecutando la

función zape/1. Tendríamos pues tres procesos: el de la consolagenerado por la llamada a lanza/0, el proceso que ejecuta zipi/1 y elproceso 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á unerror que terminará con el proceso. Como está enlazado a zipi/1, esteproceso también finalizará su ejecución.

Visto desde la consola:

> gemelos:lanza().

zipi - 0okzape - 0zipi - 1zape - 1zipi - 2zape - 2zipi - 3zape - 3zipi - 4zape - 4zipi - 5zape - 5zipi - 6>

Page 90: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 90/189

Page 91: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 91/189

Procesos

75

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 código del módulogemelos_lanzador sino que el error se produce durante el procesamientode uno de los mensajes del lanzador. En concreto, al enviarle elmensaje error al lanzador éste lanza una excepción produciendo la caídaautomática de los tres procesos.

Importante

Para que la finalización de un proceso provoque que todossus enlaces también finalicen, debe producirse una finalizaciónpor error. Si un proceso finaliza su ejecución de forma normaly satisfactoria, queda finalizado y desenlazado del resto deprocesos pero los demás no finalizan. En otras palabras, paraque un proceso enlazado sea finalizado por otro, el proceso queprovoca la caída de los procesos en cascada debe de haberacabado con un error de ejecución.

7. Monitorización de Procesos

En contraposición al enlace vital, el enlace informativo o monitorizacióntal y como se conoce en Erlang, permite recibir el estado de cada procesocomo mensaje. Este mecanismo permite que podamos conocer si unproceso sigue activo o si ha finalizado su ejecución, ya sea por un erroro de forma normal. Este tipo de enlace es diferente al anterior quesimplemente propaga los errores haciendo que se produzcan en todoslos procesos enlazados.

Un ejemplo simple del paso de mensajes cuando un proceso finaliza sepuede ver a través de este sencillo código:

> {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 esperahasta que le llegue un mensaje. Al enviarle hola , el proceso finaliza

Page 92: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 92/189

Page 93: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 93/189

Procesos

77

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 código en consola de la siguienteforma:

> 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 código presente en la clausura no mantiene ningún 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 alanzarlo de nuevo, tal y como se observa en los mensajes reavivando hijoen ....

8. Recarga de código

Uno de los requisitos con los que se desarrolló la máquina virtual deErlang fue que el código pudiese cambiar en caliente sin afectar sufuncionamiento. El mecanismo para cambiar el código es parecido al quese realiza con los lenguajes de scripting con algunos matices.

Page 94: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 94/189

Procesos

78

Quizás sea un poco extraño encontrar este tema en un capítulo dedicadoa procesos, pero nos parece apropiado ya que la recarga de códigoafecta directamente a los procesos. La recarga de código afecta más a un

proceso que lo emplea de forma continua (como es el código base delproceso), que a otro que lo emplea de forma eventual (funciones aisladasque se emplean en muchos sitios).

Pondremos un ejemplo. Teniendo este código:

-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 código como decostumbre:

> 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 código de loop/0 en ejecución yatiende a cada petición que se le envía al proceso. La función loop/0 asu vez llama, de forma fully qualified , a la función code_change/0. Estaforma de llamar a la función le permite a la máquina virtual de Erlangrevisar si hay una nueva versión del código en el fichero BEAM y, en casode ser así, recargarla.

Importante

Erlang puede mantener hasta dos instancias de código enejecución. Si tenemos un código ejecutándose que no se llamade forma  full qualified , aunque cambiemos el código BEAM nose recargará. Pero si se lanza otro proceso nuevo, se hará con lanueva versión del código. En ese momento habrá dos instanciasdiferentes de un mismo código. Si se volviese a modificar elcódigo, el sistema debe de extinguir la versión más antigua delcódigo para quedarse sólo con las dos últimas, por lo que losprocesos antiguos con el código más antiguo serían eliminados.

Page 95: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 95/189

Procesos

79

Si cambiamos el código 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 ejecución, hasta que no provocamosuna segunda ejecución no se ha producido la recarga del código nicomenzado a ejecutar el nuevo código.

Es bueno saber que podemos hacer que la recarga de código se hagabajo 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, haciendouna llamada. Después cambiamos el código modificando el texto queimprime por pantalla el mensaje y lo compilamos con la orden erlc.

Una vez hecho esto podemos provocar la recarga del códig enviando elmensaje update desde consola fácilmente:

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

Page 96: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 96/189

Procesos

80

Esta vez la llamada update nos ahorra el tener que hacer otra llamadaadicional para que se ejecute el código nuevo.

9. Gestión de ProcesosComo hemos dicho desde el principio, Erlang ejecuta su código dentrode una máquina virtual, por lo que posee su propia gestión de procesos,de la que ya comentamos sus ventajas e inconvenientes.

En este apartado revisaremos las características de que disponemos parala administración de procesos dentro de un programa. Aunque ya hemosvisto muchas de estas características como la creación, vinculacióny monitorización, nos quedan otras como el listado, comprobación y

eliminación.Comenzaremos por lo más básico, la eliminación. Erlang nos proveede una función llamada exit/2 que nos permite enviar mensajesde terminación a los procesos. Los procesos aceptan estas señales yfinalizan su ejecución. El primer parámetro es el PID que es el datoque requiere exit/2 para finalizar el proceso. El segundo parámetroes opcional y representa el motivo de la salida. Por defecto se envía elátomo normal. Su sintaxis por tanto es:

exit(Pid, Reason).

Por otro lado processes/0 nos proporciona una lista de procesosactivos. Con process_info/1 obtenemos la información sobre unproceso dado el PID e incluso mediante process_info/2 conun parámetro que indica la información específica de la lista depropiedades3: enlaces con otros procesos (links), información de lamemoria usada por el proceso (memory), la cola de mensajes (messages),por quién está siendo monitorizado (monitored_by) o a quién monitoriza(monitors), el nombre del proceso (registered_name), etc.

10. Nodos ErlangLa máquina virtual de Erlang no sólo tiene la capacidad de gestionarmillones de procesos en un único nodo, o de facilitar la comunicaciónentre procesos a través de paso de mensajes implementado a nivelde proceso, sino que también facilita la comunicación entre lo que seconoce como nodos, dando al programador la transparencia suficientepara que dos procesos comunicándose entre nodos diferentes secomporten como si estuviesen dentro del mismo.

3Toda esta información puede ser consultada, con mayor detalle de la siguiente dirección: http://www.erlang.org/doc/man/erlang.html#process_info-2

Page 97: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 97/189

Procesos

81

Cada nodo es una instancia en ejecución de la máquina virtual de Erlang.Esta máquina virtual posee la capacidad de poder comunicarse con otrosnodos siempre y cuando se cumplan unas características concretas:

• El nodo se debe haber lanzado con un nombre de nodo válido.

• 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 conexión por clave,a la que se conoce como cookie . La cookie es una palabra de paso quepermite a un nodo conectarse con otros nodos siempre que compartanla misma cookie .

Un ejemplo de lanzamiento de un nodo Erlang, desde una terminal seríael siguiente:

erl -sname test1 -setcookie mitest

Si lanzamos esta línea para test1 y test2, veremos que el símobolode sistema de la consola de Erlang se modifica adoptando el nombredel nodo de cada uno. En caso de que el nombre de la máquina enla que ejecutamos esto fuese por ejemplo bosqueviejo, tendríamosdos nodos en estos momentos levantados: test1@bosqueviejo ytest2@bosqueviejo.

El nombre propio del nodo se obtiene a través de la función internanode/0. Los nodos a los que está conectado ese nodo se obtienen conla función interna nodes/0. Los nodos de un cluster se obtienen con laforma:

[node()|nodes()]

Desde la consola podemos usar el siguiente comando para conectar losdos nodos:

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

11. Procesos RemotosHasta ahora, cuando empleábamos la función interna spawn/1

generábamos un proceso local, que se ejecutaba en el nodo que

Page 98: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 98/189

Procesos

82

corre la función. Si tenemos otros nodos conectados, podemos realizarprogramación paralela o distribuida, lanzando la ejecución de losprocesos en otros nodos. Es lo que se conoce como un proceso remoto.

Se puede lanzar un proceso remoto con la misma función spawn/1

agregando como primer parámetro el nombre del nodo donde queremoslanzar el proceso. Por ejemplo, si queremos lanzar un proceso que semantenga a la escucha para dar información en el cluster montado porlos dos nodos que lanzamos en el apartado anterior, podríamos hacerlocon el siguiente código:

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

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

Any -> Anyend.

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 función init/1, se agrega el nombre del nodo que se pasacomo parámetro a spawn/2. La comunicación la podemos realizarnormalmente como en todos los casos anteriores que hemos visto sinproblemas. No obstante, el PID devuelto, a diferencia de los vistosanteriormente, tiene su primera parte distinta de cero lo que indica queestá corriendo en otro nodo. Los procesos en nodos remotos no se puederegistrar con la función interna register/2, es decir, no se les puedeasociar un nombre y por ello, son sólo accesibles desde el nodo que loslanzó.

Page 99: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 99/189

Procesos

83

12. Procesos Locales o Globales

Todos los procesos que hemos registrado hasta ahora eran locales. Siqueremos que un proceso sea accesible desde diferentes nodos deberegistrarse como proceso global.

El lanzamiento del proceso se realiza como hasta ahora, lo único que varíaes la forma en la que se registra su nombre. Debe usarse el módulo globalcon global:register_name/2. El acceso a un proceso así registradose realiza como hasta ahora, a través del nombre. La accesibilidad existedesde 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 elotro 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 envío del mensaje lo podemos realizar a través del PID o a travésde la función send/2 del módulo global. En todo caso, obtenemos lacapacidad de tener accesibilidad a los procesos remotos desde cualquiernodo del cluster.

Nota

Hay muchos casos en los que el módulo global puede tener unrendimiento bastante bajo, o incluso hasta defectuoso. Por estarazón han aparecido sustitutos como gproc 4 (que requiere delparcheo de parte del código OTP de Erlang), o módulos que norequieren de ninguna modificación en la base como nprocreg 5.

4 https://github.com/uwiger/gproc5 https://github.com/nitrogen/nprocreg

Page 100: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 100/189

Page 101: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 101/189

Procesos

85

Nota

A través de multicall/3 en lugar de call/4, del módulo rpc 

podemos envíar el código a cada uno de los nodos conectadosen el cluster.

14. Diccionario del ProcesoPara finalizar y dar por terminado este capítulo, indicar que Erlangdispone de un diccionario de datos que puede ser empleado paramantener datos propios del proceso. Podríamos considerarlo comoatributos propios del proceso.

Estos datos pueden ser manejados a través del uso de las siguientesfunciones internas:

get/0, get/1

Cuando se indica sin parámetros se obtienen todos los datoscontenidos dentro de ese proceso. El formato de esta devolución esuna lista de propiedades que puede ser manejada con las funcionesdel módulo proplists .

Cuando se indica un parámetro, se toma como clave y se retornaúnicamente el valor solicitado.

get_keys/1

Se emplea para obtener todas las claves cuyos valores son losindicados como único parámetro de la llamada a la función.

put/2

Almacena el par clave-valor pasados como parámetros, siendo elprimero la clave y el segundo el valor.

erase/0, erase/1

Sin parámetros se encarga de eliminar todas las ocurrencias deldiccionario. Es muy útil para limpiar completamente el diccionario.Con el parámetro clave , se encarga únicamente de eliminar el valorcorrespondiente a esa clave.

Este diccionario es útil para poder desarrollar procesos en los quequeramos manejar atributos, para modificar o eliminar elementos delmismo. Proporciona un depósito de datos por proceso que nos puedeayudar a mantener información de estado entre llamadas a un mismoproceso.

Page 102: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 102/189

86

Capítulo 6. ETS, DETS y FicherosEscribir es recordar, pero leer también es recordar.

—François Mauriac 

Uno de los puntos importantes en un lenguaje de programación esla gestión de ficheros. Los ficheros tienen innumerables usos, desdeescritura de logs, hasta el almacenamiento o lectura de datos oconfiguraciones en formatos como CSV, XML o YAML, pasando por loscontenidos multimedia: imágenes en formato PNG, o vídeos de tipo AVI.Necesitamos pues los mecanismos que nos permitan realizar todas lasoperaciones con ficheros (renombrar, copiar, mover, etc.).

Erlang provee funciones básicas y muy simplificadas de acceso a ficherosy directorios. Nos permite realizar la lectura de un fichero en textoplano formateado como datos de Erlang (listas, tuplas, átomos, números,etc.). Puede además emplear tablas ETS (Erlang Term Storage) para elalmacenaje en disco o, empleando directamente DETS (Disk Erlang TermStorage), acceder a un directorio para su procesado.

En este capítulo nos adentraremos en cada aspecto referente a losficheros y las tablas ETS y DETS, a la lectura y escritura de ficheros detexto y binarios, y a los mecanismos que nos da Erlang para navegar pordirectorios.

1. ETSLas siglas ETS se refieren a Erlang Term Storage , o almacenaje de términosde Erlang. Los términos ya los habíamos revisado anteriormente, por loque sabemos que se trata de tuplas, en las que el primer elemento de latupla actúa como clave.

La razón para crear las tablas ETS fue la de poder almacenar gran cantidadde datos con un tiempo de acceso siempre constante, ya que en loslenguajes funcionales el tiempo de acceso a la información suele serfunción logarítmica. Otro motivo fue el proveer al desarrollador de un

modo de extraer, almacenar y tratar la información con los mecanismospropios del lenguaje1. Además, para que el uso de este sistema fuesemás rápido, las funciones para manejar las funcionalidades de ets  seencuentran en formato de BIF.

Por todo ello, el almacenaje de términos Erlang constituye unaherramienta fundamental de gestión de la información con Erlang,especialmente cuando el tamaño de la información es elevado y senecesita optimizar los tiempos de acceso.

1A diferencia de llamadas a sistemas, como el SQL, las tablas ETS se quería que fuesen tratadas condirectivas, sentencias y funciones del lenguaje y no enviadas a un subsistema.

Page 103: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 103/189

ETS, DETS y Ficheros

87

1.1. Tipos de Tablas

Podemos encontrar cuatro tipos de tablas ETS dependiendo de los

algoritmos empleados para la constitución de la tabla, su almacenaje yla extracción de datos:

Conjunto (set)

Es el tipo por defecto. A semejanza de los conjuntos como conceptomatemático, cada elemento (cada clave de cada tupla) debe de serúnico a la hora de realizar la inserción dentro del conjunto. El ordeninterno de los elementos no está definido.

Conjunto ordenado (ordered_set)

Es igual que el tipo anterior, pero en este caso los datos entrantes enel conjunto son ordenados mediante la comparación de su clave conlas claves de los datos almacenados a través de los comparadores< y >, siendo el primer elemento el más pequeño.

Bolsa (bag)

La bolsa elimina la restricción de que el primer elemento ya exista,pero mantiene la propiedad de que las tuplas, comparadas en suconjunto con otras, deben de ser distintas.

Bolsa duplicada (duplicate_bag)

Igual que la anterior, pero eliminando la restricción de que las tuplasen 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 tablaspodremos elegir uno u otro tipo de tabla. Por ejemplo, si tenemos quealmacenar términos de forma ordenada para su extracción podemosemplear un ordered_set , mientras que si la información que queremosalmacenar puede llegar a repetirse podríamos optar por alguna de lasbolsas, según el grado de repetición que queramos o tengamos quepermitir para los datos.

1.2. Acceso a las ETS

Las tablas ETS son creadas por un proceso que puede hacerlo conopciones de accesibilidad que permitan su acceso por otros procesos ono. Los parámetros de seguridad que podemos emplear para garantizarel acceso o denegarlo, según el caso, son los siguientes:

private

Crea la ETS de ámbito privado. Esto quiere decir que no permite aningún otro proceso el acceso a la misma.

Page 104: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 104/189

ETS, DETS y Ficheros

88

protected

El ámbito protegido para la ETS garantiza el acceso de lectura a

todos los procesos que conozcan el identificador de la ETS, perosólo permite la escritura para el proceso que la creó.

public

Garantiza el acceso a todos los procesos, tanto para lectura comoescritura, a la ETS a través del identificador de la misma.

Nota

Una ETS mantiene, a nivel de concurrencia, siempre losparámetros de aislamiento y atomicidad íntegros, por lo que

asegura que una operación de escritura sobre un objeto deuna ETS, en caso de que sea correcta o falle lo hará de formacompleta (atomicidad). Cualquier operación de lectura sólo podráver el conjunto final de las modificaciones en caso de éxito(aislamiento).

1.3. Creación de una ETS

Para crear una tabla ETS emplearemos la función new/2 del módulo ets cuyos parámetros son el nombre de la tabla (un átomo) y las opcionespara la creación de la misma.

Las opciones están en formato de lista. Cada elemento de la listacorresponderá 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 ETSvistos: public , protected o private .

named_table

Si se especifica esta opción, el primer parámetro de la función esempleado como identificador para poder acceder a la tabla.

keypos

En caso de que queramos que la clave de la ETS no sea el primerelemento de la tupla podemos agregar esta opción de la forma:

{keypos, Pos}

Page 105: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 105/189

ETS, DETS y Ficheros

89

Siendo Pos un número entero dentro del rango de elementos de latupla.

heirEl sistema puede establecer un proceso hijo al que pasarle el controlde la ETS, de modo que si algo le sucediese al proceso que creó laETS, el proceso hijo recibiría el mensaje:

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

Y tomaría en propiedad la tabla. La configuración sería:

{heir, Pid, HeirData}

En caso de no especificar un heredero, si el proceso propietario dela tabla termina su ejecución la tabla desaparece con él.

Concurrencia

Por defecto, las ETS mantienen un nivel de concurrencia porbloqueo completo, es decir, mientras se está trabajando con la tablaningún otro proceso puede acceder a ella, ya sea para leer o escribir.No obstante, a través de la opción:

{read_concurrency, true}

Activamos la concurrencia de lectura. Esta opción es buena siel número de lecturas es mayor que el de escrituras, ya que elsistema adapta internamente los datos para que las lecturas puedanemplear incluso los diferentes procesadores que pueda tener lamáquina.

Para activar la concurrencia en la escritura, se debe emplear laopción siguiente:

{write_concurrency, true}

La activación de la escritura sigue garantizando tanto la atomicidadcomo el aislamiento. Esta opción está recomendada si el nivel deconcurrencia de lectura/escritura de los datos almacenados en laETS provocan excesivo tiempo de espera y fallos a consecuencia deeste cuello de botella. La nota negativa, vuelve a ser la penalizaciónexistente al realizar las escrituras concurrentes.

compressed

Los datos de la ETS se comprimen para almacenarse en memoria. Altrabajar sobre datos comprimidos los procesos de búsqueda y toma

Page 106: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 106/189

ETS, DETS y Ficheros

90

de datos son más costosos. Esta opción es aconsejable cuando seamás importante el consumo de memoria que la velocidad de acceso.

Como ejemplos de creación 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 cualquierorden dentro de la lista de opciones, y se pueden poner tantas como senecesite.

Importante

Ante el uso de dos opciones que colisionen, como el hecho deemplear conjuntamente la opción bag y la opción set , el sistemaempleará la última leída de la lista de opciones. Por ejemplo, eneste caso:

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

Las opciones que predominan finalmente y las que se quedaránconfiguradas son bag y public .

1.4. Lectura y Escritura en ETS

Una vez que tenemos creada una ETS podemos comenzar a trabajar conella. Para agregar elementos podemos emplear la función insert/2

cuyo primer parámetro es el identificador de la tabla (o su nombre encaso de named_table ), siendo el segundo parámetro un término o unalista de términos para su inserción.

Un ejemplo para nuestra bolsa creada anteriormente sería:

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

Con esta llamada hemos introducido un término en el que la clave es rojodentro de la bolsa . Si queremos ver el contenido de la tabla, podemosemplear la función match. Esta función emplea dos parámetros: elprimero es el nombre de la ETS, y el segundo es el patrón que debede cumplir el dato para ser mostrado. De momento, daremos '$1' es elcomodín que nos permite sacar todos los datos:

Page 107: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 107/189

ETS, DETS y Ficheros

91

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

Si insertamos algunos elementos más podemos ver cómo se vanalmacenando 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 ycomo se puede ver en el listado. Para extraer un elemento concreto dadoel identificador de la tupla podemos emplear la función 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 recursión. Si llegamos al final nosdevolverá el átomo '$end_of_table' .

Un ejemplo de esto se puede ver en el siguiente módulo:

-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 función main/0, veremos como nos retorna todolo insertado dentro de bolsa . Igualmente, si creamos una nueva ETS,podemos emplear la función show_all/1para listar todo su contenido.

1.5. Match: búsquedas avanzadas

En la sección anterior vimos que el listado general se podía conseguir conuna forma específica de la función match. Esta función, a través del usode los patrones, nos permite mucha mayor potencia a la hora de rescatardatos de la ETS.

Page 108: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 108/189

ETS, DETS y Ficheros

92

La teoría de la concordancia para las ETS se puede emplear tantopara funciones select/2, como para las funciones match/2. Estaconcordancia se basa en pasarle la información al núcleo de ETS para

realizar la extracción. Requiere que se puedan identificar variables comotal o bien con el uso de comodines.

Para esto se definen dos átomos que tienen una semántica especial parael gestor de las ETS. Son las llamadas variables sin importancia 2 y elcomodín. Las variables sin importancia se pueden especificar como '$0' ,'$1' , '$2' , ...; La numeración sólo es relevante en caso de especificar laforma en la que se obtendrán los resultados (para las funciones comoselect/2).

El otro tipo de átomo con significado específico para las ETS es el

comodín '_'. Ya vimos en su momento que el signo de subrayado se utilizapara indicar que el dato en esa posición no interesa.

Rescatando el ejemplo anterior, vemos que habíamos escrito comoparámetro de la función match/2 la siguiente expresión:

'$1'

Al no tener $1 forma de tupla, esta expresión de la variable sinimportancia concuerda con toda la tupla al completo. Si pusiéramos enel match lo siguiente:

{'$1',255,'_','_'}

Veremos que extraemos el valor rojo, ya que es el único que cumple lacondición de concordancia de tener en su segunda posición el valor 255.Como la variable sin importancia sólo la hemos situado en la primeraposición, sólo recibiremos esta.

Nota

Si empleamos la funciónmatch_object/2 en lugar dematch/2se retornará siempre el objeto completo. La concordancia setendrá en cuenta sólo a nivel de elección y no a la hora deorganizar los datos para su devolución.

Por último, vamos a ver el uso de la funciónselect/2, como una funciónmás avanzada que nos da la posibilidad, no sólo de enviar una tuplade concordancia, sino también una parte de guardas y la proyección (elcómo se visualizarán en el resultado). Esta función nos da para las ETS la

2El nombre de variable sin importancia  es una traducción prestada del inglés don't care  al que hacereferencia el sitio Learn You Some Erlang [http://learnyousomeerlang.com/].

Page 109: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 109/189

ETS, DETS y Ficheros

93

misma potencia que nos brindan las listas de compresión sobre las listasque ya vimos en Sección1.4, “Listas” del Capítulo2, 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 eltercero el que se encarga de especificar la proyección de elementos.

Comenzaremos con este ejemplo:

{{'$0','$1','$2','$3'},[{'<','$1',0}],['$0']

}

He separado en cada línea cada uno de los tres parámetros que se debenenviar para cumplir con la especificación de concordancia. En la primeralínea se puede ver que no se ha realizado ninguna primera criba, sinoque se aceptan todas las ETS que tengan ese número de tuplas.

El segundo parámetro contiene una operación. Como se puede observarel formato es igual al conocido como calculadora polaca, la operaciónes el primer elemento que se encuentra y los operandos los que vienena continuación. Volviendo a nuestro ejemplo concreto la condición quedebe de cumplir la tupla es que su elemento '$1' sea mayor que cero.

Por último, en el tercer elemento, realizamos una proyección paradevolver como resultado único el primer elemento de la tupla(precisamente '$0' el identificador o clave).

Otro ejemplo más complejo o completo se deja a la investigación dellector:

{{'$1','$2','$4','$8'},[{'andalso',

{'==','$2',0},{'==','$8',0}

}],[ '$$' ]

}

1.6. Eliminando tuplas

Para eliminar una o varias tuplas, se utiliza la función delete/2.Esta función permite eliminar una clave concreta de una ETS dada, unfuncionamiento muy parecido al de la función lookup/2.

ets:delete(bolsa, verde)

Page 110: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 110/189

ETS, DETS y Ficheros

94

También cabe la posibilidad de realizar una eliminación completa de latabla con la función delete/1, o bien el vaciado de sus elementos conla función delete_all_objects/1.

Si lo que queremos es eliminar una serie de objetos específicos lopodemos realizar a través de la función match_delete/2. Esta funciónacepta como parámetros la ETS, y el patrón de elementos a eliminar, aigual que en la función match/2 ya vista.

1.7. ETS a fichero

Una forma de leer la información de una ETS desde un fichero y volvera almacenarla en un fichero es a través de las funciones tab2file/2 yfile2tab/1.

La función 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 cabeceraen la que se almacenan las opciones con las que fue creada, para quecuando se vuelva a leer el fichero, la ETS se instancie de la misma formaen la que se guardó.

La función 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 sucontenido, tal y como se guardó.

El almacenamiento de archivos es un buen sistema para poder gestionarlos datos de una ETS de manera persistente. No obstante hay que teneren mente que siempre pueden surgir problemas. Por ejemplo el tamañode la ETS puede superar el de la memoria que se puede emplear, o bien elsistema puede fallar antes de que se haya podido guardar la informaciónen el fichero. Incluso podría ser que el fichero se corrompiera durante

su escritura. Para evitar estos problemas necesitamos un sistema quetrabaje directamente con el fichero y que la robustez para haber previstoeste tipo de problemas. En Erlang este sistema son las DETS.

2. DETSLas DETS son ETS que se almacenan en disco (Disk Erlang Term Storage ).Al tratarse también de almacenaje de términos, poseen un interfazal programador muy parecido al de las ETS. El medio de tratamientoy almacenamiento de la información es distinto. Las DETS puedenmantener persistencia de información mientras que las ETS no la tienen.

Page 111: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 111/189

ETS, DETS y Ficheros

95

El sistema DETS se suele emplear cuando se requieren almacenar concierta persistencia información en forma de términos Erlang y cuyofichero de almacenaje no exceda los 2 GiB de espacio.

Este sistema de almacenaje es el mismo que emplea Mnesia3, el motorde base de datos que integra OTP y que viene por defecto con Erlang.Mnesia, como motor de base de datos, no sólo posee la capacidad detrabajar con términos para su almacenaje, sino que proporciona otroselementos como transacciones, consultas, distribución y fragmentaciónde tablas, por lo que, puede ser un entorno más complejo y potente quesi sólo se requieren almacenar términos.

2.1. Tipos de Tablas

Al igual que las ETS, las DETS se pueden crear de varios tipos y compartenestos tipos con las ETS, a excepción de los conjuntos ordenados que nose incluyen de momento por no haber encontrado una forma óptima derealizar 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 comoconcepto matemático, cada elemento (cada clave de cada tupla)

debe de ser única a la hora de realizar la inserción del elementodentro del conjunto. El orden interno no está definido.

Bolsa (bag)

La bolsa elimina la restricción de que el identificador de la tupla seaigual, pero mantiene la propiedad de que las tuplas, comparadas ensu conjunto con otras, deben de ser distintas.

Bolsa duplicada (duplicate_bag)

Igual que la anterior, pero eliminando la restricción 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 día de hoy (en la revisión R15 de Erlang/OTP), no existe libreríaque permita escribir de forma ordenada los términos hacia disco.Está pendiente y posiblemente en futuras liberaciones veamosque finalmente se agrega a esta lista el conjunto ordenado.

3En este libro no se tratará Mnesia, porque sino el texto se nos extendería unas decenas de páginas másy no conseguiríamos abarcarlo como se merece.

Page 112: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 112/189

ETS, DETS y Ficheros

96

2.2. Crear o abrir una DETS

Esta operación se realiza mediante la función open_file/2. Estecomando no sólo sirve para crear la DETS, sino que además una vez que laDETS exista, nos permite abrirla y seguir usándola en otras ejecuciones.Veamos los parámetros de la función para abrir o crear una DETS:

open_file(Name, Args) -> {ok, Name} | {error, Reason}

El parámetro Name será el que le dé nombre a la DETS. El nombre debede ser un átomo como ocurre con las ETS. Este dato será el que se soliciteen el resto de funciones para poder acceder a la DETS.

Las opciones que se pueden agregar como segundo parámetro son lassiguientes:

{access, Access}

Como acceso son válidos 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 dela DETS. Es decir, el tiempo en el que se realiza una sincronización

entre lo que se mantiene en memoria y el disco. Si se especificainfinity , el autoguardado es deshabilitado. La opción por defecto es180000 (3 minutos).

{min_no_slots, Slots}

Es un ajuste de rendimiento que permite especificar en la creaciónde la tabla el número de claves estimado que serán almacenadas.El valor por defecto es 256.

{max_no_slots, Slots}

El número máximo de slots que será usado. El valor por defecto ymáximo permitido es de 32000000.

{keypos, Pos}

La posición dentro del término en el que se encontrará la clave dela tupla.

{file, File}

El nombre del fichero que se usará. Por defecto se toma el nombrede la DETS para la escritura del fichero.

Page 113: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 113/189

ETS, DETS y Ficheros

97

{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 abrirlarealizando el volcado a disco cuando se cierra. Por defecto estacaracterística no está activa ( false ).

{repair, true | false | force}

Le dice al sistema que debe ejecutar la reparación de la DETS alabrirla, en caso de que no se hubiese cerrado correctamente. Pordefecto está activa (true ). En caso de que se indique  false  y serequiera reparación, se retornará el error:

{error, {needs_repair, File}}

El valor force quiere decir que la reparación se llevará a cabo aunquela tabla haya sido cerrada correctamente.

{type, Tipo}

El tipo de la tabla, tal y como vimos en la sección anterior.

Importante

Para no perder información, es importante que siempre cerremos

la DETS de forma apropiada, a través de la función close/1. Sino lo hacemos, al volver a ejecutar el programa, es seguro quese requerirá una reparación del fichero e incluso podrían llegar aperderse 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, nosproporcionan información sobre la DETS abierta dado su nombre.

La última apertura, como se puede ver, nos origina un error. Esto esdebido a que se ha intentado abrir un fichero llamado conjunto que no

Page 114: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 114/189

Page 115: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 115/189

ETS, DETS y Ficheros

99

En este caso, la DETS sí es vaciada (se eliminan todos sus elementos) y acontinuación se inserta la ETS tal cual dentro de la DETS.

Nota

En ambos casos, el orden en el que se guardan los elementos esindeterminado, tanto de DETS a ETS, como en el caso opuesto.

3. FicherosEn este apartado revisaremos lo que se puede hacer desde Erlang conlos ficheros. Para ello, vamos a diferenciar el tratamiento de los ficherosentre los dos tipos existentes: binarios y de texto.

Los ficheros de texto tienen un tratamiento especial, ya que seinterpretan algunos caracteres especiales como fin de fichero, salto delínea, e incluso se pueden tomar ficheros formateados de cierta formapara que se carguen como formas de datos de Erlang.

En cambio, los ficheros binarios, tienen un tamaño definido y todos susbytes son iguales, es decir, no tienen ningún significado especial, aunquese 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 accesoErlang es susceptible de poder ser abierto. También podemos generarnuevos ficheros dentro de ese mismo sistema de ficheros.

Los ficheros que podemos abrir o crear son ficheros que puedencontener texto, imágenes, audio, vídeo, documentos formateados comolos RTF, o ficheros binarios de cualquier otro tipo.

La función de que disponemos para poder abrir o crear ficheros es lasiguiente:

open(Filename, Modes) -> {ok, IoDevice} | {error, Reason}

Como primer parámetro tenemos el nombre del fichero (Filename ), y elsegundo parámtro corresponde a una lista en la que se pueden agregartantas 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 agregación al final (append ). El caso de exclusive , es usadopara crear el fichero, devolviendo un error en caso de que ya exista.

Page 116: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 116/189

ETS, DETS y Ficheros

100

raw

Abre el fichero mucho más rápido, ya que ningún proceso de Erlang

se encarga de manejar el fichero. Sin embargo, este modo de trabajotiene limitaciones, como que las funciones del módulo io no puedenser empleadas o que sólo el proceso que haya abierto el ficheropuede utilizarlo.

binary

Las operaciones de lectura retornarán listas binarias en lugar delistas.

{delayed_write, Size, Delay}

Los datos se mantienen en un buffer hasta que se alcanza el tamañoindicado por Size o hasta que el dato más antiguo en el buffer es demás allá del tiempo especificado en Delay , entonces se escriben adisco. Esta opción se emplea para decrementar los accesos a discoy, por lo tanto, intentar incrementar el rendimiento del sistema.

{read_ahead, Size}

Activa el buffer de lectura para las operaciones de lectura que soninferiores al tamaño definido en Size . Igual que en el caso anterior,decrementa el número de llamadas al sistema para acceso a disco,por lo que aumenta el rendimiento.

compressed

Crea o abre ficheros comprimidos con gzip. Esta opción puedecombinarse con read o write , pero no ambas.

{encoding, Encoding}

Realiza la conversión automática de caracteres para y desde untipo específico. La codificación por defecto es latin1. Las tablasde codificación permitidas se pueden revisar en la documentaciónoficial de la función open/2

4.

Un ejemplo de apertura de un fichero tan famoso5 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-25Para los que usan Debian o Ubuntu, o alguna distribución derivada de estas, es frecuente encontrar elfichero /etc/debian_versionen el sistema de ficheros.

Page 117: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 117/189

ETS, DETS y Ficheros

101

Nota

Vemos que el retorno de la primera operación que se realiza

correctamente, nos devuelve un PID. Al no haber empleado laopción raw se crea un proceso Erlang intermedio que se encargade la información del fichero y de realizar los accesos de lecturay escritura.

Al intentar abrir un fichero para escritura hemos obtenido un error deacceso (eaccess ) debido a la falta de permisos, ya que un usuario normalno tiene permisos para escribir en ese fichero.

Para cerrar el fichero, y con ello liberar el proceso que se mantiene a laespera de indicaciones para tratar dicho fichero, debemos de emplear la

función close/1. En los ejemplos anteriores, sería 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 yaque esto repercute, no sólo en un uso innecesario de los recursos

de los descriptores de ficheros

6

, sino también de procesos, ya quecada fichero abierto de un modo no raw lleva asociado un procesoErlang.

3.2. Lectura de Ficheros de Texto

Los ficheros de texto son los que el sistema interpreta dando unsignificado concreto a ciertos caracteres especiales. El caracter de avancede línea (\n), es tomado como un salto de línea y el caracter de retorno decarro (\r), en caso de ir seguido al avance de línea, es ignorado y tomadocomo parte del salto de línea.

Con esta consideración, funciones como read_line/1, se encargan deleer una línea del fichero. Leen tantos bytes como sean necesarios hastallegar a un salto de línea o el final del fichero.

Como el fichero que vimos en el ejemplo de apertura de ficheros esde tipo texto, podemos ir leyendo línea a línea mediante la funciónread_line/1 hasta el fin de fichero y luego cerrarlo, como se ve en elsiguiente ejemplo:

6Son las estructuras del sistema operativo que se emplean para designar que un programa tiene unfichero abierto.

Page 118: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 118/189

ETS, DETS y Ficheros

102

> {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 paraalmacenarlo en una variable de texto, podemos emplear la funciónread_file/1:

> file:read_file("/etc/debian_version").{ok,<<"wheezy/sid\n">>}

Nota

Al igual que hemos empleado read_line/1, podemos empleario:get_line/2 para realizar la lectura, pasando como primerparámetro el identificador del fichero abierto.

Por último, si el contenido del fichero que queremos leer contieneelementos de Erlang, como términos o listas, separados por un puntocada uno de los elementos base que conforman el documento, estepuede ser leído y evaluado como datos Erlang directamente.

Esto se realiza con al función consult/1. Esta función puede leer unfichero como el que se muestra a continuación:

{nombre, "Manuel"}.{apellido1, "Rubio"}.{apellido2, "Jimenez"}.

Leyendo este fichero (datos_personales.cfg) desde consola:

> file:consult("datos_personales.cfg").

Page 119: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 119/189

Page 120: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 120/189

ETS, DETS y Ficheros

104

fichero abierto y, como segundo parámetro, el tamaño del fichero queserá leído.

Aunque podemos emplear otras funciones, esta es la más genérica y nospermitirá realizar las lecturas de los ficheros sin problemas. Si queremosleer la totalidad del fichero es más aconsejable emplear la funciónread_file/1, ya que nos auna la apertura, lectura y cierre del ficheroen una sola función.

Un ejemplo de lectura de una imagen sería 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 leídos de nuestro fichero binario imagen se muestran como unalista binaria de enteros. En concreto hemos leído 16 bytes del principiodel archivo PNG. Podemos leer 8 bytes que conforman la firma del PNGy los 8 que contienen la cabecera del PNG en la siguiente forma (comopodemos ver en la wikipedia7):

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

> file:close(Pid).ok

Siguiendo las directrices de la especificación de los ficheros PNG, hemospodido extraer, gracias a las listas binarias y al tratamiento que se puederealizar con los bits, el tamaño de la imagen y muchos otros datos quepodríamos también pasar por pantalla.

Es aconsejable realizar el tratamiento de ficheros binarios siempre através de listas binarias. En el ejemplo de la lectura del PNG hemos

7 http://en.wikipedia.org/wiki/Portable_Network_Graphics

Page 121: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 121/189

Page 122: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 122/189

Page 123: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 123/189

ETS, DETS y Ficheros

107

[{{bof,0}, 4},{{eof, -24}, 4},

{{bof, 12}, 4}]

En este caso, en lugar de especificar contenido para escribir,especificamos el tamaño para leer a partir de la posición dada.

Importante

En caso de emplear listas de caracteres, hay que tener especialcuidado con los caracteres de UTF-8, ya que algunos empleandos bytes para su almacenaje en lugar de sólo uno y estopuede provocar que el cómputo de la posición sea erróneo (osusceptible de errores).

Para evitar estos errores, aconsejo emplear el acceso aleatoriotan sólo en ficheros binarios, ya que las unidades están mejordefinidas en este caso.

4. Gestión de Ficheros

Además de todo lo visto anteriormente para la creación, modificacióny lectura de un fichero, podemos realizar más acciones aún con

estos ficheros, como puede ser: renombrarlos, cambiar sus permisos,propietario8, copiar el fichero, truncarlo o eliminarlo.

4.1. Nombre del fichero

Doy por supuesto que todos conocemos que, los nombres de los ficherosse componen por la ruta en la que se ubica el fichero, su nombrey extensión. En Erlang, el módulo  filename  nos permite obtener lainformación correspondiente a un nombre de fichero: su ruta, su nombre,su nombre raíz (sin extensión) y/o su extensión.

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

8El cambio de permisos y propietario depende de cada sistema operativo y los permisos en sí que tengael usuario que lanzó la ejecución del programa.

Page 124: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 124/189

Page 125: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 125/189

ETS, DETS y Ficheros

109

y reiniciar sus punteros a la posición cero, esto lo podemos realizarmediante el uso de la función truncate/1.

4.3. Permisos, Propietarios y GruposOtro de los aspectos relevantes cuando gestionamos ficheros, son suspermisos y su pertenencia a un usuario o grupo. El cambio de lospermisos se puede realizar mediante la función change_mode/2. Elcambio de propietario se hace mediante change_owner/2 y el cambiode grupo a través de change_group/2.

La función change_mode/2 permite cambiar los permisos del fichero.Como primer parámetro se pasa el nombre del fichero y como segundoparámetro el modo que se desea establecer, en modo numérico. En modooctal, tenemos esta tabla de permisos:

Valor numérico Permiso Usuario

8#00400 Lectura Propietario

8#00200 Escritura Propietario

8#00100 Ejecución Propietario

8#00040 Lectura Grupo

8#00020 Escritura Grupo

8#00010 Ejecución Grupo

8#00004 Lectura Otros

8#00002 Escritura Otros

8#00001 Ejecución Otros

Por lo que si queremos que el fichero logo.png tenga permisos delectura 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 través de la funciónchange_owner/2o change_owner/3 si además queremos cambiar elgrupo. Los parámetros de UID y GID se dan en formato entero9.

Hay una función que engloba todas las funciones del módulo  file para la gestión de usuarios, grupos y permisos y permite realizar

9En los sistemas de tipo Unix este dato se puede ver en /etc/passwd donde hay una correspondenciaentre el nombre del usuario y su UID.

Page 126: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 126/189

ETS, DETS y Ficheros

110

todas las modificaciones en una sola acción, tanto para la lectura:read_file_info/1; como para la escritura: write_file_info/2.Vamos a verlas un poco más en detalle:

read_file_info/1

Permite leer las propiedades de un fichero retornando un registroen el que aparecen datos como la fecha y hora de creación, fecha yhora de modificación y fecha y hora del último acceso, además delos permisos, tipo de fichero y tamaño 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 parámetro, un registro detipo  file_info y rellenarlo con los datos del fichero que deseemosmodificar.

5. Gestión de Directorios

Hasta el momento hemos visto como trabajar con ficheros, su contenidoya sea de tipo texto o de tipo binario, así como la gestión propia delos ficheros (copia, renombrado, eliminación, ...), ahora vamos a tratar lagestión de los directorios.

Los directorios nos permiten organizar nuestros ficheros de una formamás categorizada. Para sistemas que trabajan con miles de ficheros estono es una opción sino una necesidad, ya que, en el terreno informático

no hay recursos infinitos e incluso el número de ficheros que puedenalbergarse en un directorio está limitado10.

Como la gestión de los directorios, e incluso la de los ficheros se puederealizar desde un programa, un sistema que cree muchos ficheros puedeparticionar estos en directorios y subdirectorios, de modo que el accesoa cada directorio sea más rápido que en el caso de tener un directoriocon miles de ficheros.

10Hay sistemas de ficheros que establecen este límite a 1024 y otros que permiten miles o millonesde ficheros por directorio, pero esto no es nada aconsejable, ya que el tratamiento y gestión del propiodirectorio o de los propios ficheros puede ser extremadamente lento.

Page 127: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 127/189

ETS, DETS y Ficheros

111

Veremos a continuación las funciones relativas a la gestión de directoriosbajo los conceptos en los que se emplean.

5.1. Directorio de TrabajoLas rutas que indicamos para los nombres de ficheros las podemosindicar de forma absoluta o relativa tanto para su apertura como parasu gestión. La ruta absoluta nos indica dónde se encuentra un ficheromientras 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 través dellamadas específicas al sistema. La ruta de trabajo podemos extraerla conget_cwd/0del módulo file . Cualquier referencia a fichero que hagamos

de forma relativa será siempre relativa a esta ruta.

Podemos cambiar la ruta de trabajo mediante la función set_cwd/1,donde especificamos cuál será la nueva ruta de trabajo. Esto afectará atodas las rutas relativas que se empleen a partir del cambio.

Es un método bastante frecuente el cambiar la ruta para la ejecuciónde un código específico y volver inmediatamente a la ruta anterior, algocomo esto:

Dir = file:get_cwd(),file:set_cwd("/miruta"),

%% ejecuta_codigo...file:set_cwd(Dir).

La ruta inicial se sitúa en el directorio en el que nos encontrásemos alejecutar la consola de Erlang.

5.2. Creación y Eliminación de Directorios

Una de las acciones básicas con los directorios es la de su creación yeliminación. Comenzaremos con el primer caso, la creación. La creaciónse 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 realizarel comando:

mkdir -p /miruta/nuevo1/nuevo2/midir

Con lo que no sólo se crea un directorio, sino todo los necesarios hastallegar al último indicado en la ruta pasada como parámetro. Esto no lorealiza make_dir/1. Esta función debe de recibir una ruta existente y

Page 128: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 128/189

ETS, DETS y Ficheros

112

creará el último directorio que se indique en la ruta, siempre que noexista ya.

En caso de que quisiéramos crear un directorio con todos susdirectorios padres, en caso de que no existan, podemos emplear lafunción ensure_dir/1 del módulo  filelib. Esta función crea todos losdirectorios 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 funciónmake_dir/1 no puede crear el directorio final, dir1. La funciónensure_dir/1, crea ambos directorios, primero prueba y después dir1dentro de prueba .

Importante

El parámetro de ensure_dir/1 debe de terminar en barra paraque cree hasta el último directorio, ya que la función está creadacon la idea de que se pueda pasar como parámetro la ruta de unfichero (con el nombre del fichero incluído) y cree el directoriopara 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 lasfunciones is_list/1, podemos hacer uso de las funciones del módulo filelib: is_dir/1 o is_file/1.

Esto nos permite realizar funciones que nos permitan realizar un proceso

previo de validación, por ejemplo, en caso de las configuraciones en lasque 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 (tamaño delfichero) o last_modified/1 (la última fecha de modificación) paraagregar más semántica o funcionalidad al código.

Page 129: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 129/189

Page 130: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 130/189

Page 131: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 131/189

Comunicaciones

115

que lleguen a su destino y los acuses de recibo para asegurar deque el paquete es recibido correctamente. En este nivel veremosdos protocolos: TCP y UDP.

Nivel de aplicación

Este es el nivel más alto que podemos encontrar en comunicación.Aquí se definen y usan protocolos como: HTTP, FTP, SMTP, POP3,IMAP, etc.

1.1. Direcciones IP

Una dirección IP se representa mediante un número binario de 32 bits(según IPv4). Las direcciones IP se pueden expresar como números de

notación decimal: se dividen los 32 bits de la dirección en cuatro octetos.El valor decimal de cada octeto puede estar entre 0 y 2551.

En la expresión de direcciones IPv4 en decimal se separa cada octetopor un punto. Cada uno de estos octetos puede estar comprendidoentre 0 y 255, salvo algunas excepciones. Los ceros iniciales, si loshubiera, se pueden obviar. Ejemplo de representación de dirección IP:164.12.123.65

Importante

Las direcciones IP en Erlang se emplean a través de un formato detupla formada por cuatro elementos enteros. Esta forma es en laque generalmente trabaja el módulo inet que es el que se encargade las comunicaciones, tanto para conexiones cliente, como paraservidor:

{127,0,0,1}

Este sería el formato de IP para 127.0.0.1. La función inet:ip/1nos permite realizar la conversión del formato de texto al formatode tupla.

Hay tres clases de direcciones IP que una organización puede recibirde parte de la Internet Corporation for Assigned Names and Numbers (ICANN): clase A, clase B y clase C. En la actualidad, ICANN reservalas direcciones de clase A para los gobiernos de todo el mundo2 ylas direcciones de clase B para las medianas empresas. Se otorgandirecciones de clase C para todos los demás solicitantes. Cada clase dered permite una cantidad fija de equipos (hosts).

1El número binario de 8 bits más alto es 11111111 y esos bits, de derecha a izquierda, tienen valoresdecimales de 1, 2, 4, 8, 16, 32, 64 y 128, lo que suma 255 en total.2Aunque en el pasado se le hayan otorgado a empresas de gran envergadura como, por ejemplo, HewlettPackard.

Page 132: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 132/189

Comunicaciones

116

• 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 alos hosts, de modo que la cantidad máxima de hosts es 224 menos dos:

las direcciones reservadas de broadcast (tres últimos octetos a 255) yde red (tres últimos octetos a 0), es decir, 16.777.214 equipos.

• En una red de clase B, se asignan los dos primeros octetos paraidentificar la red, reservando los dos octetos finales (16 bits) paraque sean asignados a los equipos, de modo que la cantidad máximade equipos es 216 (de nuevo menos dos), lo que equivale a 65.534equipos.

• En una red de clase C, se asignan los tres primeros octetos paraidentificar la red, reservando el octeto final (8 bits) para que sea

asignado a los equipos, de modo que la cantidad máxima de equiposes 28 (menos dos), o 254 equipos.

• La dirección 0.0.0.0 es utilizada por las máquinas cuando estánarrancando o no se les ha asignado dirección.

• La dirección que tiene a cero su parte destinada a equipos sirve paradefinir la red en la que se ubica. Se denomina dirección de red.

• La dirección que tiene a uno todos los bits de su parte de equipo sirvepara comunicar con todos los equipos de la red en la que se ubica. Se

denomina dirección de broadcast.• Las direcciones 127.x.x.x se reservan para pruebas de

retroalimentación. Se denomina dirección de bucle local o loopback.

Hay ciertas direcciones en cada clase de dirección IP que no estánasignadas y que se denominan direcciones privadas. Las direccionesprivadas pueden ser utilizadas por los equipos que usan traducción dedirección de red (NAT) para conectarse a una red pública o por los hostsque no se conectan a Internet. En una misma red no pueden existir dosdirecciones iguales, pero sí se pueden repetir en dos redes privadas que

no tengan conexión 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 tamaño amenudo se usa TCP/IP. Por ejemplo, los bancos pueden utilizar TCP/IP para conectar los cajeros automáticos que no se conectan a la red

Page 133: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 133/189

Page 134: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 134/189

Comunicaciones

118

El puerto activo toma de la numeración de puertos un número y lo reservapara establecer la comunicación con el puerto pasivo. El número delpuerto activo puede ser elegido o no dependiendo del protocolo de

transporte.

La comunicación se identifica para el sistema operativo con el par depuertos activo/pasivo además de la dirección IP tanto de origen como dedestino. Esto hace posible que a un puerto pasivo se pueda conectar másde un puerto activo.

Nota

En la jerga de los sistemas de comunicación existen algunaspalabras clave que se emplean para determinar ciertos aspectos

de la comunicación o los elementos que la componen. La palabraanglosajona socket  (traducida como zócalo o conector) es lapalabra que se suele emplear para indicar una conexión. Cuandose establece un puerto pasivo mediante TCP se suele decir que elservidor escucha mientras que si es mediante UDP se dice que elservidor está enlazado a ese puerto.

En el esquema cliente-servidor existe un servidor que se mantiene a laespera de una petición entrante y un cliente de forma activa realizaslas peticiones al servidor. El servidor emplearía un puerto pasivo paraestablecer la comunicación mientras que el cliente usaría uno activo.

Como hemos ido comentando a lo largo de la sección hay dos protocolospara transporte que emplean los puertos de comunicación:

TCP

Es orientado a conexión. Requiere que el cliente formalice laconexión con el servidor y esta se mantiene hasta que una de lasdos partes solicita la desconexión o durante un envío se produzcaun tiempo de espera agotado.

UDP

UDP es un protocolo de datagramas. Un datagrama puede serenviado hacia un servidor pero no se comprueba el estado derecepción. No se espera respuesta del mismo. El tratamiento de estetipo de paquetes carga menos la red y los sistemas operativos perosi la red no es fiable puede existir pérdida de información.

2. Servidor y Cliente UDP

Erlang provee un módulo llamado gen_udp que nos permite establecerun puerto pasivo para mantenernos enlazados y recibir paquetes

Page 135: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 135/189

Comunicaciones

119

entrantes. También permite emplear un puerto activo para el envío deun 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 servidorlo hará de forma pasiva para recibir mensajes. El cliente lo hará deforma activa para enviarlos y tener la posibilidad de recibir respuesta delservidor.

El módulo gen_udp aprovecha las capacidades propias de los procesosenviando cada paquete recibido al proceso que solicitó el enlace con elpuerto.

Hay tres funciones que emplearemos con mucha frecuencia en la

construcción de servidores y clientes UDP: open/1 o open/2, send/4y close/1.

La función open/2 se presenta:

open(Port, Opts) -> {ok, Socket} | {error, Reason}

Puede recibir como segundo parámetro opciones que permiten variar laforma en la que establecer el enlace con el puerto. Las opciones son:

list | binaryIndica si se quiere recibir el paquete como una lista o un binario. Elvalor por defecto es list .

{ip | ifaddr, ip_address()}

La dirección 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 serán pasados alproceso (true ) o no ( false ) o si se hará sólo la primera vez (once ). Elvalor por defecto es true .

{reuseaddr, true | false}

Permite reutilizar el puerto. No lo bloquea. Por defecto esta opciónestá deshabilitada.

Page 136: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 136/189

Comunicaciones

120

Nota

Hay disponibles muchas opciones más a las que no entraremos yaque son conceptos más avanzados o muy específicos, con lo quesalen del ámbito de explicación de este capítulo. Si desea másinformación sobre la función gen_udp:open/2 puede revisar lasiguiente dirección:

http://www.erlang.org/doc/man/gen_udp.html#open-2

Pondremos en práctica lo aprendido. Vamos a escribir un módulo queenlace 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 código se inicia mediante la función start/1 indicando un númeroentero correspondiente al puerto enlazado. Lanza un nuevo proceso que

ejecuta la función init/1encargada de abrir el puerto y pasar el controla la función loop/1 que se encarga de atender los paquetes que vayanllegando.

En el ejemplo hemos usado un registro formado por los cuatro datos quenos envía cada paquete UDP además del identificador:

{udp, Socket, IP, InPortNo, Packet}

La definición de estos datos es la siguiente:

Page 137: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 137/189

Comunicaciones

121

Socket

El manejador retornado por la función open/1 u open/2.

IP

La dirección 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 queel puerto se encuentra enlazado emplearemos la funcióninet:i/0quenos proporciona información sobre las comunicaciones:

> udpsrv:start(2020).ok> inet:i().Port [...] Recv Sent Owner Local Address [...] State Type593 [...] 0 0 <0.34.0> *:2020 [...] BOUND DGRAMok

La salida nos muestra el proceso (Owner) que tiene enlazado (State =:=BOUND) el puerto 2020 (Local) de tipo UDP (Type =:= DGRAM). Nosproporciona también otros datos estadísticos como los bytes recibidos(Recv) y enviados (Sent).

Podemos escribir estas líneas en la consola de Erlang para probar elcódigo del servidor:

> {ok, Socket} = gen_udp:open(0).{ok,#Port<0.618>}21> gen_udp:send(Socket, {127,0,0,1}, 2020, "hola mundo!").okrecibido({127,0,0,1}): "hola mundo!"

La operación de conexión se ha realizado abriendo una comunicacióncon la función gen_udp:open/1 pasando como parámetro el puerto 0.El puerto 0 se usa para indicar que queremos que el sistema operativoseleccione un puerto automáticamente por nosotros. Podemos recurrira ejecutar de nuevo inet:i/0 para ver las conexiones abiertas y lasestadísticas:

> inet:i().Port [...] Recv Sent Owner Local Address [...] State Type593 [...] 11 0 <0.34.0> *:2020 [...] BOUND DGRAM

Page 138: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 138/189

Comunicaciones

122

618 [...] 0 11 <0.38.0> *:33361 [...] BOUND DGRAMok

Ahora vemos dos líneas. La primera sigue siendo la del servidor y lasegunda pertenece al cliente. Por los datos estadísticos vemos que lainformación que ha enviado el cliente (columna Sent) es la que harecibido el servidor (columna Recv).

Vamos a modificar el código del servidor para que retorne al cliente unacadena 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 función gen_udp:send/4para enviar al remitenteuna respuesta enviando un texto fijo al cliente. Podemos probarlo enconsola 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!").okrecibido({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 lafunción gen_udp:open/2 hemos empleado el parámetro de opcionespara indicar a gen_udp que no envíe el paquete recibido al proceso. Al

Page 139: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 139/189

Comunicaciones

123

ejecutar gen_udp:recv/2 es cuando se obtiene la información quellega del servidor.

3. Servidor y Cliente TCPPara establecer comunicaciones TCP en Erlang disponemos del módulogen_tcp. En TCP el servidor escucha de un puerto y se mantieneaceptando peticiones entrantes que quedan conectadas tras suaceptación. La dinámica está protocolizada de forma que un servidorestablece una escucha en un puerto específico con posibilidad de envíode opciones a través de la función listen/2 cuya definición es:

listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}

Las opciones disponibles son iguales a las que se mostraron en la funcióngen_udp:open. Son las siguientes:

list | binary

Indica si se quiere recibir el paquete como una lista o un binario. Elvalor por defecto es list .

{ip | ifaddr, ip_address()}

La dirección 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 serán pasados alproceso (true ) o no ( false ) o sólo la primera vez (once ). El valor pordefecto es true .

{reuseaddr, true | false}

Permite reutilizar el puerto. No lo bloquea. Por defecto esta opciónestá deshabilitada.

Nota

Hay disponibles muchas opciones más a las que no entraremos yaque son conceptos más avanzados o muy específicos, con lo quesalen del ámbito de explicación de este capítulo. Si desea másinformación sobre la funcióngen_tcp:listen/2 puede revisareste enlace4

4 http://www.erlang.org/doc/man/gen_tcp.html#listen-2

Page 140: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 140/189

Comunicaciones

124

Si ponemos en escucha el puerto 2020 y volvemos a listar el estado dela 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 Type609 [...] 0 0 <0.32.0> *:2020 [...] LISTEN STREAMok

El siguiente paso será mantener el proceso a la espera de una conexiónentrante. Es el proceso que se llama aceptación y se realiza con lafunción accept/1. Si lo ejecutamos en la consola de Erlang el sistemase quedará bloqueado a la espera de una conexión entrante:

> {ok, SockAceptado} = gen_tcp:accept(Socket).

La función emplea la variable Socket creada en listen/2 para esperarnuevas conexiones entrantes. Cuando una conexión entrante llega lafunción accept/1 acepta la conexión y finaliza su ejecución retornandootra variable SockAceptado. Este socket  se empleará para comunicarsecon el cliente y obtener información del mismo.

En otra consola podemos realizar la conexión entrante. Emplearemos lafunción connect/3 que tiene la siguiente sintaxis:

connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}

Como parámetros se indica la dirección IP a la que conectarse (Address en formato tupla), el puerto al que conectarse (Port ) y las opciones paraestablecer la comunicación (Options ). Las opciones son las mismas quese describieron para la función listen/2.

Abrimos otra consola y establecemos una comunicación local entreambos 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 Type599 [...] 0 0 [...] *:38139 *:2020 CONNECTED STREAMok

La nueva consola de Erlang sólo tiene constancia de una conexiónexistente que está en estado conectada (State) y va del puerto local38139 al puerto 2020.

Desde la nueva consola podemos enviar información al servidor a travésde la función send/2 la cual tiene la forma:

Page 141: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 141/189

Comunicaciones

125

send(Socket, Packet) -> ok | {error, Reason}

Como Socket emplearemos el que nos retornó la función connect/2.El Packet  es la información que queremos enviar. El paquete permiteformatos de lista de caracteres y lista binaria. Podemos ejecutarlo de lasiguiente manera:

> gen_tcp:send(Socket, "hola mundo!").

En el servidor no vemos de momento nada por consola. Si ejecutamosflush/0 aparecerán los mensajes recibidos al proceso de la consola5.

Si agregamos a las opciones de la función listen/2  {active, false}  el

mensaje no es enviado al proceso sino que espera a que lo recibamosa través del uso de la función recv/2. Esta función tiene la siguientesintaxis:

recv(Socket, Length) -> {ok, Packet} | {error, Reason}

El Socket es el valor de retorno obtenido tras la ejecución de la funciónaccept/1. Length es el tamaño máximo que se espera recibir delpaquete. En caso de que el paquete sea mayor que el tamaño una nuevaejecución de recv/2 recogerá el siguiente trozo de información. En caso

de indicar un tamaño cero se recibe todo el paquete sin limitación detamaño.

Para establecer el diálogo entre servidor y cliente sólo necesitamosemplear las funciones send/2 y recv/2 para realizar la comunicaciónbidireccional. En el momento en el que se desee finalizar lacomunicación por cualquiera de las dos partes emplearíamos la funciónclose/1.

Importante

El socket  que se estableció para escucha se puede igualmentecerrar conclose/1. Conviene cerrar los puertos antes de finalizarla ejecución de los servidores para liberar los puertos empleados.

4. Servidor TCP Concurrente

La comunicación TCP se basa en la interconexión de dos puntos. EnErlang se conecta cada socket a un proceso para recibir información por

5Recordemos que la consola es un proceso que se mantiene a la espera de recibir eventos. Todos loseventos que reciba la consola son interceptados por esta. Véase apéndice B para más información.

Page 142: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 142/189

Comunicaciones

126

lo que hasta que la conexión entre cliente y servidor no se cierra ningúnotro cliente puede ser atendido por el servidor. Este problema no sepresenta en comunicaciones UDP. En TCP cada conexión servidora genera

un socket de conexión con el cliente específico.

Para que el proceso de servidor no permanezca bloqueado atendiendola conexión del primer cliente que conecte generamos un nuevo procesopara atender esa petición entrante. De esta forma el proceso principalqueda liberado para aceptar más peticiones y generar nuevos procesosa medida que vayan llegando nuevas peticiones de clientes.

Para que el nuevo socket  generado sepa que tiene que enviarsus paquetes al nuevo proceso hay que emplear la funcióncontrolling_process/2, que tiene la forma:

controlling_process(Socket, Pid) -> ok | {error, Reason}

Escribiremos un pequeño módulo para comprobar cómo funciona.Llamaremos al módulo 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 función start/1 se encarga de lanzar en un proceso aparte laejecución de la función srv_init/1. El cometido de esta funcióninicializadora es establecer la escucha en un puerto TCP. El bucle deejecución para el servidor se basa en aceptar una conexión, generar un

Page 143: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 143/189

Comunicaciones

127

nuevo proceso, pasarle el control de la conexión con el cliente al nuevoproceso y vuelta a empezar.

La generación del nuevo proceso tiene como función de bucle principal aworker_loop. Esta función integraría el protocolo a nivel de aplicaciónpara interactuar con el cliente. En nuestro ejemplo esperamos a recibirun mensaje y lo retornamos precedido de la palabra Eco.

El siguiente código 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).

La función cli_send/2 permite conectarse a un puerto local6, enviarun mensaje y esperar por el retorno antes de finalizar la comunicación.

Hasta el momento todo funciona como la versión anterior. No obstante,hemos agregado en el servidor un retraso de 5 segundos que nos ayudaráa ver la concurrencia en ejecuciones múltiples de cli_send/2. Lo

podemos realizar con varias consolas o a través de un código como elsiguiente:

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 código genera 10 procesos que ejecutan la función cli_send/2

enviando el mensaje i=I , siendo I el valor pasado por foreach/2 a cada

uno de los procesos.La ejecución muestra como todos los procesos llegan a recibir el mensajeen el servidor y quedan esperando por el resultado 5 segundos después.

5. Ventajas de inet 

Erlang no sólo dispone de funciones para manejar las comunicaciones anivel transporte. El módulo inet a través de la funciónsetopts/2provee

6En este ejemplo no hemos empleado direcciones IP, por lo que se emplea por defecto la IP local o127.0.0.1.

Page 144: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 144/189

Comunicaciones

128

la capacidad de interpretar los paquetes recibidos a través de TCP o UDPy enviarlos como mensaje al proceso ya procesados.

Según la documentación de inet 7

los formatos que procesa son: CORBA,ASN-1, SunRPC, FastCGI, Line, TPKT y HTTP.

Nota

La decodificación la realiza únicamente a nivel de recepción, elenvío deberemos de componerlo nosotros mismos y enviarlo conla función de send/2 de gen_tcp.

Para construir nuestro propio servidor HTTP y aprovechar la característicaque nos provee inet sólo tendríamos que agregar la opción a la función

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 petición.Como hemos levantado un puerto TCP y le hemos configurado lascaracterísticas de HTTP, vamos a abrir un navegador con la siguiente URL:

http://localhost:8080/

En la consola veremos que ya prosigue la ejecución:

{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 primerelemento http. Como en los casos de tcp el segundo parámetro es Socket .Como tercer parámetro puede aparecer otra tupla cuyo primer parámetroes:

7 http://www.erlang.org/doc/man/inet.html#setopts-2

Page 145: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 145/189

Comunicaciones

129

http_request

Si se trata de la primera línea de petición. Esta tupla tendrá 4

campos: http_request , método HTTP (GET, POST, PUT o DELETE entreotros), URI y versión HTTP en forma de tupla de dos elementos. Unejemplo:

{http_request, 'GET', {abs_path,"/"},{1,1}}

http_header

Las siguientes líneas a la petición son las líneas de cabecera. Que seestructuran en una tupla de 5 campos: http_header , bit de cabecera,nombre de la cabecera, valor reservado (undefined ) y valor de lacabecera.

http_eoh

Este dato se transmite en forma de átomo. Indica que la recepciónde cabeceras ha finalizado.

A continuación vemos un ejemplo completo. Presenta las peticionesrecibidas por pantalla junto con su contenido:

-module(httpsrv).-export([start/1]).

-define(RESP, "HTTP/1.1 200 OKContent-Length: 2Content-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} ->

Page 146: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 146/189

Comunicaciones

130

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

En la función worker_loop/1 cuando se recibe http_eoh se puede

ver que se modifica el tipo de paquete para poder recibir el contenido.Además vemos que se diferencian bien los mensajes que se reciben detipo http de los que son de tipo tcp.

Nota

Si empleamos el parámetro {active, false} para emplear la funciónrecv/2 en lugar de receive hay que tener presente que el retornode la función recv/2 será: {ok, HttpPacket} , mientras que elretorno de receive será: {http, Socket, HttpPacket} .

Page 147: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 147/189

131

Capítulo 8. Ecosistema ErlangLa construcción exitosa de toda máquina depende 

de la perfección de las herramientas empleadas.Quien sea un maestro en el arte de la fabricación de herramientas poseerá la clave para la construcción

de todas las máquinas.—Charles Babbage 

Un ecosistema es un ambiente en el que conviven elementos enun espacio relacionandose entre sí. En software se ha tomado estadefinición para definir al conjunto de herramientas y sistemas quepermiten realizar software.

En Erlang lo usaremos para identificar el uso de unas herramientas juntocon sus buenas prácticas a la hora de desarrollar proyectos de software.

Para este fin daremos un repaso a la herramienta de construcción rebar que ha llegado a convertirse en un estándar dentro de la comunidad deErlang.

1. Iniciar un Proyecto

A lo largo de los capítulos hemos realizado la mayor parte del códigoen la consola de Erlang y vimos la organización del código interno y larealización de módulos. Aún no hemos comentado la forma que debetener nuestro espacio de trabajo, los directorios que es conveniente creary la disposición de los ficheros dentro de estos directorios.

La herramienta rebar es la más empleada entre las utilidades de tercerosdel mundo Erlang. La empresa Basho1 es la desarrolladora principal deesta herramienta aunque cada día hay más contribuidores al proyecto.

Un proyecto en Erlang/OTP debe disponer de una estructura base como

la siguiente:src

Este directorio contendrá el código fuente. Todos los ficheros cuyaextensión sea .erl.

ebin

Aquí se almacenarán los ficheros de tipo .beam, es decir lacompilación de nuestra aplicación.

1Basho Technologies es una empresa estadounidense que desarrolla la base de datos Riak.

Page 148: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 148/189

Ecosistema Erlang

132

include

Los ficheros que se almacenan en este directorio son los de tipo

cabecera .hrl.priv

Cuando el proyecto requiere de ficheros específicos para funcionarse introducen en este directorio ficheros como certificados, páginasHTML, hojas de estilo CSS o códigos JavaScript entre otros.

Nota

Hay más directorios por defecto para proyectos Erlang/OTP comoc_src donde se alojan los ficheros de extensión escritos en C,test para los códigos de pruebas de EUnit o CommonTest odepses donde se bajan otros proyectos de terceros para incluir sucódigo dentro de nuestro proyecto.

Crearemos un proyecto que ilustre cómo organizar los ficheros delcódigo y cómo ejecutar ese mismo código de forma autónoma.

Para esta tarea nos ayudaremos de rebar . Creamos los tres directoriosbase y pasamos a instalar rebar .

1.1. Instalar rebar La instalación de rebar se basa en descargar el código de su repositorio yejecutar el script bootstrap. El sistema generará el script rebar en esemismo directorio. Este script se puede copiar a un directorio del PATH olocalmente dentro del proyecto que estemos desarrollando.

$ git clone git://github.com/basho/rebar.git$ cd rebar$ ./bootstrapRecompile: 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 scriptanywhere 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 pornuestro PATH. Normalmente como super usuario en sistemas de tipoUnix2 en una ruta como /usr/bin, /usr/local/bin o /opt/local/bin.

2Sistemas Unix o tipo Unix como BSD, Linux, MacOS X u OpenSolaris entre otros.

Page 149: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 149/189

Ecosistema Erlang

133

Para asegurarnos de que es accesible podemos ejecutarlo en la consola.

Nota

La utilidad rebar se encuentra también disponible para Windowsa través del repositorio bifurcado ( fork ) de IRONkyle3.

Recomiendo que los proyectos iniciales y el aprendizaje se llevena cabo en sistemas tipo Unix como MacOS o GNU/Linux por elmotivo de que la mayoría de soporte y desarrollos se realizan enestos sistemas.

1.2. Escribiendo el Código

Vamos a crear un proyecto que consistirá en un servidor web al que se lesolicitarán ficheros y en caso de existir en el directorio priv se retornarásu contenido.

El desarrollo lo realizaremos en dos ficheros. El primer módulo seencargará de establecer la escucha para el servidor web y atender laspeticiones:

-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

Page 150: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 150/189

Ecosistema Erlang

134

{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 código listado fue visto en la Sección4, “Servidor TCP Concurrente”del Capítulo7, Comunicaciones . Solo agregaremos la llamada al módulo fileserver .

El segundo módulo se encargará de buscar el fichero solicitado yretornarlo como texto identificando su tipo. El código es el siguiente:

-module(fileserver).

-export([send/1]).

-define(RESP_404, <<"HTTP/1.1 404 Not FoundServer: Erlang Web ServerConnection: Close

">>).

-define(RESP_200, <<"HTTP/1.1 200 OKServer: Erlang Web ServerConnection: CloseContent-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_404end.

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

Page 151: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 151/189

Ecosistema Erlang

135

_ -> <<"text/plain">>end.

Con esto ya tenemos el código preparado. Solos nos falta escribir ladefinición necesaria para que rebar  pueda identificar la aplicación yconstruir el producto final. Crearemos el fichero en el directorio src conel nombre webserver.app.src:

{application, webserver , [

{description , "Erlang Web Server"},

{vsn , "1.0"},

{applications ,[kernel, stdlib, inets

]}]}.

El nombre que tendrá la aplicación.Como descripción se especifica un texto. Es deseable que no seamuy extenso. Se puede poner el nombre completo de la aplicación.La versión de la aplicación. Se puede especificar de la forma quese desee.Aplicaciones que deben de iniciarse antes de iniciar la nuestra.Dependencias.

Nota

Como versión en la línea de vsn podemos emplear las palabrasclave: git , hg , bzr , svn o {cmd, Cmd} . Las primeras indican al sistemaque tome el tag o el número de revisión del sistema de control deversiones. La última indica que ejecute el comando contenido enCmd para obtener la versión.

En la página de referencia de app4 podemos ver una lista más completay detallada de las opciones que permite el fichero para iniciar unaaplicación.

2. Compilar y LimpiarUna vez que tenemos el directorio src creado podemos compilarlo todoejecutando el comando: rebar compile. El comando rebar se encarga decrear el directorio ebin y depositar los ficheros beam dentro de él:

$ rebar compile==> webserver_simple (compile)Compiled src/webserver.erlCompiled src/fileserver.erl

4 http://www.erlang.org/doc/man/app.html

Page 152: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 152/189

Ecosistema Erlang

136

El espacio de trabajo es como se puede observar a continuación:

El directorio ebin contiene la compilación de los códigos listados enla sección anterior. El fichero webserver.app.src se analiza y secompleta para generar el fichero webserver.appdentro del directorioebin.

Si queremos ejecutar el código, podemos iniciar la consola de lasiguiente forma:

$ erl -sname webserver -pa ebin

(webserver@bosqueviejo)1> webserver:start(8888).<0.39.0>

De esta forma tenemos el código en ejecución. Para eliminar estosficheros generados ejecutamos el comando rebar clean.

3. Creando y lanzando una aplicaciónEn la sección anterior vimos que el lanzamiento del código escrito sehacía de forma manual. Si desarrollamos una aplicación de servidor hayque poder lanzar esta aplicación de forma automática.

Erlang proporciona al programador una forma de realizar esto a través deun comportamiento5 denominado application.

Para ello necesitamos crear otro fichero de código dentro del directoriosrc . Llamaremos a este fichero webserver_app.erl y pondremos elsiguiente contenido:

-module(webserver_app).

5Los comportamientos (behaviours ) son un mecanismo de inversión de control (IoC) que posibilita lacreación de código abstracto más concreto para el usuario. Estos serán vistos en mayor profundidad enel Volumen II.

Page 153: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 153/189

Ecosistema Erlang

137

-behaviour(application).

-export([start/0, start/2, stop/1]).

start() ->application:start(webserver).

start(_StartType, _StartArgs) ->{ok, webserver:start(8888)}.

stop(_State) ->ok.

Este módulo dispone de tres funciones. Las funciones start/2 ystop/1 son requeridas por el comportamiento application, mientras questart/0 la emplearemos para la línea de comandos.

En el fichero webserver.app.srcsolo debemos de agregar una nuevalínea que indique qué módulo se hará cargo de las llamadas propias dela aplicación para su inicio y fin:

{application, webserver, [{description, "Erlang Web Server"},{vsn, "1.0"},{applications,[

kernel, stdlib, inets]},

{mod, {webserver_app, []}}]}.

Línea que indica el módulo de comportamiento application que sehará cargo del inicio y parada de la aplicación.

En la consola agregaremos un par de argumentos más6:

$ erl -sname test -pa ebin -s inets -s webserver_app \-noshell -detached

El comando se encarga de dar un nombre al nodo (-sname), decir dondese encuentra el código que queremos lanzar (-pa), arrancar la aplicacióninets (-s) y la aplicación webserver . Indicamos además que no queremos

que se ejecute una consola o shell (-noshell) y que se ejecute en segundoplano (-detached).

4. DependenciasEn Internet existen repositorios con miles de librerías para Erlang.Los más representativos son github.com7 y bitbucket8. En estos sitios

6Los argumentos usados para la línea de comandos se pueden revisar en el Apéndice B, La línea de comandos .7 https://github.com8 https://bitbucket.org

Page 154: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 154/189

Ecosistema Erlang

138

podemos encontrar librerías para conectar con MySQL, PostgreSQL,Memcached, o frameworks web como ChicagoBoss o Nitrogen, oframeworks para crear servidores web como cowboy o mochiweb entre

otras muchas.

La herramienta rebar posibilita a través de su fichero de configuraciónque podamos instalar en nuestro proyecto una librería externa con muypoco esfuerzo.

El código del fichero fileserver.erl muestra una función llamadamimetype/1. Esa función es insuficiente para cubrir todos los tiposposibles de ficheros que pudiésemos utilizar en nuestra aplicación.Podemos emplear en su lugar la librería mimetypes 9.

Para ello generaríamos el fichero de configuración rebar.configen la

ruta raíz del proyecto y con el siguiente contenido:

{deps, [{mimetypes, ".*",

{git, "https://github.com/spawngrid/mimetypes.git","master"}

}]}.

La especificación de la aplicación la cambiamos también para agregar lanueva 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 laaplicación mimetypes  para cumplir con la dependencia. La funciónstart/0 quedaría 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

Page 155: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 155/189

Ecosistema Erlang

139

stop(_State) ->ok.

Por último, cambiamos el código escrito para que en lugar de tener el usode nuestra función mimetype/1 emplee las que provee la librería:

-module(fileserver).-export([send/1]).

-define(RESP_404, <<"HTTP/1.1 404 Not FoundServer: Erlang Web ServerConnection: Close

">>).

-define(RESP_200, <<"HTTP/1.1 200 OKServer: Erlang Web ServerConnection: CloseContent-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_404end.

Antes de compilar debemos de lanzar el siguiente comando paradescargar las dependencias que hemos indicado que necesitamos ennuestro 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.xrlCompiled src/mimetypes_parse.yrlCompiled src/mimetypes_loader.erlCompiled src/mimetypes_scan.erlCompiled src/mimetypes_sup.erlCompiled src/mimetypes_app.erl

Page 156: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 156/189

Ecosistema Erlang

140

Compiled src/mimetypes.erlCompiled src/mimetypes_parse.erl==> webserver_deps (compile)Compiled src/webserver_app.erl

Compiled src/webserver.erlCompiled src/fileserver.erl

Nota

El comando rebar get-deps se emplea para descargar lasdependencias mientras que rebar del-deps se encarga deeliminarlas. Este último comando es útil para realizar una limpiezadel proyecto junto con rebar clean:

$ rebar del-deps==> mimetypes (delete-deps)

==> webserver (delete-deps)$ rebar clean==> webserver (clean)

Para lanzar de nuevo la aplicación agregaremos la ruta de lasdependencias 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 más precisos:

$ netstat -tln | grep 8888tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN$ curl -i http://localhost:8888/rebar.configHTTP/1.1 200 OKServer: Erlang Web ServerConnection: CloseContent-type: application/octet-streamContent-lenght: 119

{deps, [{mimetypes, ".*",

{git, "https://github.com/spawngrid/mimetypes.git",

"master"}}

]}.

5. Liberar y DesplegarEl desarrollo de software tiene su culminación cuando el software puedeser instalado en sistemas en producción. Liberar el código consiste endejar preparado el producto para su instalación. Este debe de poderseempaquetar, construir y lanzar de forma fácil y simple. El despliegueconsiste en el procedimiento de instalación de este código liberado.

Page 157: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 157/189

Page 158: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 158/189

Ecosistema Erlang

142

{sub_dirs, ["apps/*"]}.

Nota

El directorio apps  se emplea cuando se requieren escribirprogramas con varias aplicaciones. El comando rebar generaterequiere que esta estructura exista para realizar la liberación.

El fichero reltool.configes la configuración que necesita el sistemadenominado reltool para generar la liberación. Las posibles entradas deconfiguración que se pueden agregar al fichero son muy numerosas. Paraprofundizar más el tema puedes visitar su página de documentaciónoficial10. En este apartado recogeremos las más importantes que seagregarán 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 aplicación (primer parámetro), laversión de la aplicación (segundo parámetro) y las aplicaciones aejecutar. El listado de aplicaciones debe contener las applicacionesen el orden en el que se deben de ir iniciando cuando se arranqueel 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 deellos debe marcarse por defecto con esta opción.

{profile, development | standalone | embedded}

El perfil indica el nivel de restricción a aplicar para la copia dedependencias, librerías o binarios entre otros. Hay tres perfiles demenos a más restrictivo: development , standalone y embedded .

10 http://www.erlang.org/doc/man/reltool.html

Page 159: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 159/189

Ecosistema Erlang

143

{incl_cond, include | exclude | derived }

Indica el modo en que serán elegidas las aplicaciones que entrarán

en la liberación. Las opciones disponibles son:include

Entran todas las aplicaciones menos la que explícitamente seindique que no entre.

exclude

Entran solo las aplicaciones que se indiquen de forma explícitaque deban de entrar.

derived

Se incluyen las aplicaciones indicadas explícitamente y todassus dependencias.

{mod_cond, all | app | ebin | derived | none}

Es como incl_cond  pero a nivel de aplicación. Las opciones quepermite son:

all

Se incluyen todos los módulos de cada aplicación incluída enla liberación.

app

Se incluyen todos los módulos listados en el fichero .app yderivados11.

ebin

Se incluyen todos los módulos que estén en el directorio ebinde la aplicación y los derivados11.

derived

Se incluyen los módulos que estén siendo usados por losincluídos explícitamente.

none

No se incluye ninguno.

{app, App, [ConfList]}

Esta es la especificación individual de cada aplicación. Debe deexistir al menos la principal. Como configuración se puede indicar

11Se refiere a todos los módulos que no estén incluídos pero que reltool detecte que puedan serutilizados.

Page 160: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 160/189

Ecosistema Erlang

144

una entrada incl_cond  que aplique solo sobre la aplicación yopcionalmente otra mod_cond que actúe solo sobre la aplicación.En nuestro fichero pondremos únicamente:

{app, webserver, [{mod_cond, app}, {incl_cond, include}]}

Nota

Se pueden emplear filtros para agregar ciertos ficherossólo y según qué nivel (archivo, sistema o aplicación). Noprofundizaremos en este tema para no extendernos más y porqueeste uso es más una referencia que el lector puede encontrarfácilmente en la web oficial.

Otras entradas al mismo nivel de sys  que se pueden encontrar sontarget_dir  que indica el nombre del directorio donde se situará elresultado de la liberación y overlay que contiene comandos adicionalesa ejecutar durante la liberación. Estos comandos adicionales son deltipo crear directorio (mkdir), copiar fichero (copy) o emplear una plantilla(template) a fusionar con un fichero de variables y generar el fichero queestará 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}]}

]}.

Page 161: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 161/189

Ecosistema Erlang

145

{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 seencuentra la liberación. El comando rebar  nos proporciona en eldirectorio bin un script que nos permite lanzar el programa de variasformas:

console

En primer plano. Abre una consola y ejecuta todas las aplicacionesmientras vemos en pantalla los mensajes que imprime cada una delas aplicaciones al lanzarse.

start / stop

Estos comandos permiten iniciar y detener la aplicación que selanza en segundo plano.

ping

Hace un ping al nodo de la aplicación. En caso de que esté activo el

nodo responderá con un pong .

attach

Permite conectarse a una aplicación ejecutándose en segundoplano.

Si ejecutamos el comando start  y después ping  podremos ver que elsistema responde sin problemas. Entramos a consola a través de attachy podemos ver los mensajes de log que hayamos escrito en el fichero:

$ webserver/bin/webserver start

Page 162: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 162/189

Ecosistema Erlang

146

/tmp/rel$ webserver/bin/webserver pingpong/tmp/rel$ webserver/bin/webserver attachAttaching to /tmp/rel/webserver/erlang.pipe.1 (^D to exit)

([email protected])1>

Importante

Cuando nos conectamos a una aplicación en ejecución con attachdebemos siempre salir con la pulsación de las teclas Control+D.Si salimos interrumpiendo la consola la aplicación se detendrá.

El despliegue en un servidor u otro equipo informático se realizarácomprimiendo el resultado que se ha obtenido en webserver  ydescomprimirlo en el destino. El lanzamiento lo podemos agregar comoscript en los sistemas tipo Unix gracias a que respeta la forma de start y stop.

Nota

Una buena práctica es que cada vez que demos una versión comoterminada, hagamos una compilación en un fichero comprimidode la misma. Esto nos servirá para poder transportar nuestroproyecto a producción y crear actualizaciones.

6. Actualizando en Caliente

Una de las ventajas que reseñamos de Erlang al principio es su capacidadpara cambiar el código en caliente sin necesidad de detener la ejecucióndel programa. En la Sección 8, “Recarga de código” del Capítulo 5,Procesos vimos cómo cargar código en caliente. En esta sección veremoscómo realiza esta acción rebar para cambiar el código en caliente de todoun proyecto completo.

Como ejemplo pensemos que necesitamos modificar el ficherofileserver.erl para que responda a una solicitud con una URI /helpque retorne un texto personalizado de ayuda.

Lo primero que haremos será generar la versión 1.0 y modificar el nombredentro 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

Page 163: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 163/189

Ecosistema Erlang

147

Server: Erlang Web ServerConnection: Close

">>).

-define(RESP_200, <<"HTTP/1.1 200 OKServer: Erlang Web ServerConnection: CloseContent-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_404end

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.

También modificamos la versión dentro del ficherowebserver.app.srcpara que refleje el cambio de versión y la versiónen el fichero reltool.cfg para que sea 2.0 en lugar de 1.0.

Page 164: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 164/189

Ecosistema Erlang

148

Volvemos a generar el proyecto compilando y generando el productofinal:

$ rebar clean compile$ cd rel$ rebar generate

Tenemos dos directorios de nuestro proyecto. Uno con la versión 1.0 yotro con la versión 2.0. Es ahora cuando generamos los ficheros appup.Estos ficheros se generan por aplicación y contienen información sobrelos cambios que hay que realizar en caliente.

Dejamos que rebar generate-appups nos genere todos los ficherosnecesarios:

$ rebar generate-appups previous_release=webserver_old==> rel (generate-appups)Generated appup for webserverAppup generation complete

El fichero generado para nuestra aplicación es webserver.appup. Estefichero 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 versión que va a ser instalada.Bloque que indica las acciones a llevar para pasar de la versión 1.0a la 2.0.

Cada opción a llevar a cabo tendrá forma de tupla. La acciónload_module se refiere a la recarga del módulo que se indica (eneste caso fileserver ).Bloque de las acciones a llevar a cabo en caso de querer realizar unamarcha atrás de la versión 2.0 a la versión 1.0.

Page 165: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 165/189

Ecosistema Erlang

149

Nota

El fichero appup permite muchos más comandos. Si loscambios han sido más significativos como la agregación oeliminación de módulos se pueden emplear otros comandoscomo add_module y/o delete_module . El sistema también permitetrazar la dependencia de módulos y el orden en el que se debende ir cargando a través de las opciones PrePurge , PostPurge  yD epMods de las formas completas de las tuplas de comandos quepueden verse en la web oficial de appup12. Por ejemplo:

{add_module, filesystem},{add_module, ftp},{load_module, webserver},{code_change, [{webserver, undefined}]},

{delete_module, fileserver}

Esta forma se aplicaría cuando cambiamos fileserver.erl

por otros módulos diferentes. Primero cargamos los nuevos,recargamos el manejador, enviamos el código de cambio parael manejador13 y eliminamos el módulo antiguo. El soportepara system_code_change/4 debe de existir en el módulowebserver .

Ahora generamos el paquete de la nueva versión. Para ello utilizamos elcomando 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 ficherocontiene los binarios del programa para ejecutarse en producción, asícomo la información de cada aplicación y los ficheros para recargar cadauna de manera adecuada.

El fichero debe de copiarse en la ruta webse rver/releases . Si hemos

lanzado el código antiguo que está en webserver_old , copiamos dentrode su directorio releases este fichero. Ejecutamos:

$ cp webserver_2.0.tar.gz webserver_old/releases$ webserver_old/bin/webserver upgrade webserver_2.0Unpacked Release "2.0"Installed Release "2.0"Made Release "2.0" Permanent

12 http://www.erlang.org/doc/man/appup.html13 http://www.erlang.org/doc/man/sys.html#Mod:system_code_change-4

Page 166: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 166/189

Ecosistema Erlang

150

Importante

Para que el sistema no falle habrá que revisar la configuracióndel nombre de nodo en el fichero vm.args dentro del directorio files  antes de realizar la generación del producto final. Esrecomendable emplear sname y solo indicar el nombre del nodoeliminando el nombre de la máquina.

Si accedemos mediante navegador web a la URI /help veremos que elcódigo se ha actualizado y muestra ahora el nuevo texto de ayuda quehemos agregado.

7. Guiones en Erlang

Los guiones son un tipo de programación en la que se desarrolla uncódigo de forma rápida para servir de guión a una tarea automatizada.Normalmente por un administrador de sistemas. Dentro de las tareas másusuales de los guiones se encuentran el lanzamiento, monitorización yparada de aplicaciones.

Erlang permite la realización de este tipo de guiones a través de escript.El comando escript interpreta de forma rápida códigos Erlang que debenconstar de una función 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

catcherror:badarg -> main([])

end.

fact(1) -> 1;fact(N) -> N * fact(N-1).

La función main/1 siempre recibe una lista de cadenas de texto. Si nohay argumentos la lista está vacía.

Nota

La primera línea es conocida como shebang o hashbang . Indica elcomando con el que hay que ejecutar ese guión.

Page 167: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 167/189

Ecosistema Erlang

151

En el código no existe declaración de módulo ni exportación defunciones. Aún sin estar compilado puede hacer uso de cualquier libreríade Erlang además de todos los ejemplos de código vistos anteriormente.

Podemos probarlo así:

$ chmod +x fact$ ./factUsage: fact <number>

$ ./fact aUsage: fact <number>

$ ./fact 1212! = 479001600

El comando rebar escriptize14 se encarga tomar todos los móduloscompilados de un proyecto e introducirlos en un fichero binario yejecutable. Esto permite la distribución fácil de ese script y su instalaciónen el sistema de ficheros.

Si creamos una estructura de aplicación normal con su directorio src conel fichero fact.erl que empleamos cambiándolo 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

catcherror:badarg -> main([])

end.

fact(1) -> 1;fact(N) -> N * fact(N-1).

Agregamos también el fichero de aplicación siguiente:

{application, fact, [{description, "Factorial"},{vsn, "1.0"},{applications,[

kernel, stdlib, inets]}

]}.

14Aunque hay versiones de rebar en las que la ayuda no lo muestra existe y funciona en esas mismasfunciones.

Page 168: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 168/189

Ecosistema Erlang

152

Podemos crear nuestro guión 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 guión anterior veremosque se comporta exactamente igual. Esta forma tiene la ventaja de quese pueden incrustar más módulos, dependencias y que el código seencuentra ya compilado por lo que es más rápido que en el ejemploanterior.

8. El camino a OTPComo ya avancé en la introducción este libro consta de dos partes.En esta primera parte hemos visto todo lo necesario para conocer ellenguaje y el funcionamiento de la máquina virtual de Erlang. Hemosrepasado cómo trabajar con los proyectos. Hemos formado nuestramente a un nuevo conocimiento y a una nueva forma de hacer las cosas.Sin embargo en los proyectos profesionales de Erlang se emplea y conmucha frecuencia OTP.

Lo aprendido a lo largo de estas páginas constituye una base deconocimiento y una forma de trabajo con el lenguaje, pero ese

conocimiento debe ser ampliado a través del estudio de OTP para brindarmejores soluciones al código escrito.

Espero que el libro haya resultado útil, ameno y que la curiosidaddespertada por Erlang haya sido satisfecha e incluso las ganas de seguiraprendiendo con el siguiente volumen de este libro.

Hasta entonces, un saludo y suerte con el código.

Page 169: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 169/189

Apéndices

Page 170: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 170/189

154

Apéndice A. Instalación de ErlangTener la máquina virtual de Erlang operativa con todas sus característicases bastante fácil gracias a la gran cantidad de instaladores ydistribuciones preparadas que existen en la web. Las versiones oficialesse ofrecen desde la página web oficial de Erlang1.

En este apéndice veremos como bajar e instalar Erlang, tanto si loqueremos hacer desde paquetes listos para funcionar directamente odesde código fuente.

1. Instalación en Windows

Aunque siempre recomiendo GNU/Linux o incluso algún sistema BSDpara programar y desarrollar software, las preferencias de cada uno sondistintas y hay muchos usuarios y programadores que prefieren Windowsa cualquier otro sistema operativo.

La descarga para Windows se puede realizar desde la web de descargas2

de la página oficial de Erlang. Entre los paquetes que hay para descargarse puede encontrar Windows Binary File3. Se trata de un instalador quenos guiará paso a paso en la instalación.

La instalación en estos sistemas se divide en varios pasos. Se seleccionanlos paquetes a instalar y la ruta:

 

1 http://erlang.org/2 http://www.erlang.org/download.html3También se encuentra la versión de 64 bits para los que tengan sistemas Windows de 64 bits.

Page 171: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 171/189

Instalación de Erlang

155

Nota

La instalación de la versión R12B02 requiere de la instalación deunas DLLs que son propiedad de Microsoft. El instalador inicia unproceso de instalación para estas librerías en las que habrá queaceptar las licencias y acuerdos de uso de las propias librerías.

En un futuro se plantea la eliminación de estas librerías en favorde otras de mingw (una versión de GCC para Windows) que nospermitirán saltar esta parte.

Seguimos con la instalación hasta que el instalador nos informe de queha finalizado con éxito. En el menú de inicio veremos que se ha creadoun nuevo grupo de programas. Podemos lanzar el que tiene como título

Erlang  con el logotipo del lenguaje a su lado para que se muestre lasiguiente pantalla:

Ahora ya tenemos lista la consola de Erlang. Podemos tomar cualquierejemplo del libro para probar su funcionamiento.

2. Instalación en sistemas GNU/Linux

La mayoría de distribuciones GNU/Linux disponen de sistemas deinstalación de paquetes de forma automatizada. Erlang está disponiblepor defecto en la mayoría de estas distribuciones pero, dado que estos

paquetes en muchas de estas distribuciones se marcaron como estables

Page 172: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 172/189

Instalación de Erlang

156

hace mucho tiempo las versiones de Erlang disponibles pueden ser algoantiguas.

2.1. Desde Paquetes BinariosTenemos la opción de descargar un paquete actualizado e instalarlo enlugar del que provee por defecto la distribución que estemos usando.Los paquetes actuales se pueden descargar desde la web de ErlangSolutions:

https://www.erlang-solutions.com/downloads/download-erlang-otp

Las versiones para CentOS y Fedora se descargan en formato RPM ypueden instalarse a través de la herramienta rpm.

Las versiones para Debian, Ubuntu y Raspbian se descargan en formatoDEB y pueden instalarse a través de la herramienta dpkg.

Una vez instalado podemos ejecutar desde consola el comando erl o erlcentre otros.

2.2. Compilando el Código Fuente

Otra opción es compilar el código fuente para los sistemas en los que

no se encuentre Erlang en la última versión o se quiera disponer devarias versiones instaladas en rutas diferentes a las que se establecenpor defecto.

En este caso habrá que descargar el último archivo comprimido decódigo:

# 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 compilación requiere que se disponga en el sistema de un compiladory las librerías en las que se basa Erlang. El comando configure nos darápistas sobre las librerías que haya que instalar.

Importante

Sistemas como Ubuntu no disponen acceso directo como usuarioroot . En su lugar se debe de acceder a root a través del comandosudo. Para realizar la acción anterior sin que surjan problemas,deberemos de ejecutar antes: sudo su.

Page 173: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 173/189

Instalación de Erlang

157

3. Otros sistemas

La empresa Erlang Solutions provee paquetes de instalación para otrossistemas como MacOS X. En este sistema podemos optar por instalar estepaquete o por la instalación desde otros sistemas como MacPorts4.

En sistemas como OpenSolaris o BSD (FreeBSD, OpenBSD o NetBSD)la solución más común es instalar desde código fuente tal y como secomentó en la sección Sección2.2, “Compilando el Código Fuente” .

4 http://www.macports.org/

Page 174: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 174/189

158

Apéndice B. La línea de

comandosEl código de la mayoría de ejemplos del libro han sido desarrolladosen la consola o línea de comandos. Erlang como máquina virtualdispone de esta línea de comandos para facilitar su gestión y demostrarsu versatilidad permitiendo conectar una consola a un nodo que seencuentre en ejecución y permitir al administrador obtener informacióndel servidor en ejecución.

La línea de comandos es por tanto uno de los principales elementosde Erlang. En este apéndice veremos las opciones que nos ofrece este

intérprete de comandos para facilitar la tarea de gestión. Muchas de estasfunciones ya se han ido mostrando a través de los capítulos del libro porlo que este compendio será una referencia útil para nuestro trabajo deldía a día.

Importante

Las funciones que se listan a continuación están disponibles soloen la línea de comandos, no es posible emplearlos en el códigode un programa convencional.

1. RegistrosLos registros se comentaron en la Sección1.6, “Registros” del Capítulo2,El lenguaje . En la consola se pueden gestionar los registros a través delas siguientes funciones:

rd(R,D)

Define un registro en la línea de comandos:

> rd(state, {hits, miss, error}).

rl() / rl(R)

Muestra todos los registros definidos en la línea de comandos en elprimer caso y solo el registro pasado como parámetro en el segundocaso. La definición se muestra como se escribiría dentro de unfichero de código.

rf() / rf(R)

Elimina la definición de los registros cargados. La primera formaelimina todos los registros mientras que la segunda solo elimina elregistro pasado como parámetro R .

Page 175: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 175/189

La línea de comandos

159

rr(Modulo) / rr(Wildcard) / rr(MoW,R) / rr(MoW,R,O)

Carga los módulos de uno o varios ficheros. Los ficheros se pueden

indicar mediante el nombre de un módulo (véasem()

) o el nombrede un fichero o varios con el uso de comodines (wildcard ). Sepuede agregar un segundo parámetro que indique el registro quese desea cargar y un tercer parámetro que se usará como conjuntode opciones1.

2. MódulosIndicaremos todos los comandos referentes a la compilación, carga einformación para los módulos:

c(FoM)

Compila un fichero pasando su nombre como parámetro. El nombreproporcionado será un átomo con el nombre del módulo o unacadena que indique el nombre del fichero, opcionalmente con suruta.

l(M)

Permite cargar un módulo. Conviene recordar lo ya mencionadosobre la carga de módulos en la Sección8, “Recarga de código” delCapítulo5, Procesos .

m() / m(M)Muestra todos los módulos cargados en memoria en el primer casoe información detallada del módulo cargado en el segundo caso.Se muestra información como la fecha y hora de compilación, laruta de dónde se encuentra el módulo en el sistema de ficheros, lasfunciones que exporta y las opciones de compilación.

lc([F])

Lista de ficheros a compilar.

nl(M)Carga el módulo indicado en todos los nodos conectados.

nc(FoM)

Compila y carga el módulo o fichero en todos los nodos conectados.

y(F)

Genera un analizador Yecc, el fichero pasado como parámetro debede ser un fichero con sintaxis válida para Yecc.

1Las opciones que se pueden usar con rr/3 son las mismas que se pueden emplear para la compilación.

Page 176: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 176/189

La línea de comandos

160

3. Variables

En la línea de comandos se pueden emplear variables. Estas variablestienen el comportamiento de única asignación igual que el código quepodemos escribir en cualquier módulo. Las siguientes funciones nospermiten gestionar estas variables:

b()

Muestra todas las variables empleadas o enlazadas (binding) a unvalor en la línea de comandos.

f() / f(X)

Indica a la línea de comandos que olvide (forget) todas las variableso solo la indicada como parámetro.

4. Histórico

La consola dispone de un histórico que nos permite repetir comandosya utilizados en la consola. El histórico es configurable y contendrálos últimos comandos tecleados. El símbolo del sistema (o prompt) nosindicará el número de orden que estamos ejecutando.

Además de los comandos, la consola de Erlang también almacena losúltimos resultados. El número de resultados almacenados también esconfigurable.

Estas son las funciones que pueden emplearse:

e(N)

Repite el comando con orden N según el símbolo de sistema de laconsola.

h()

Muestra el histórico de comandos ejecutados.

history(N)

Configura el número de entradas que serán almacenadas comohistórico.

results(N)

Configura el número de resultados que serán almacenados comohistórico.

Page 177: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 177/189

La línea de comandos

161

v(N)

Obtiene el resultado de la línea correspondiente pasada como

parámetro. A diferencia dee(N)

el comando no se vuelve aejecutar, solo se muestra el resultado del comando N ejecutadoanteriormente.

5. Procesos

Estas son funciones rápidas y de gestión sobre los temas que ya serevisaron en el Capítulo5, Procesos :

bt(Pid)

Obtiene el trazado de pila del proceso en ejecución.

flush()

Muestra todos los mensajes enviados al proceso de la consola.

i(X,Y,Z)

Muestra información de un proceso dando sus números comoargumentos separados de la función. La información obtenida es elestado de ejecución del proceso, procesos a los que está enlazado,la cola de mensajes, el diccionario del proceso y memoria utilizada

entre otras opciones más.

pid(X,Y,Z)

Obtiene el tipo de dato PID de los números dados.

regs() / nregs()

Lista todos los procesos registrados (con nombre) en el nodo actualo en todos los nodos conectados respectivamente.

catch_exception(B)

Cada ejecución se realiza mediante un evaluador. Cuando se lanzauna excepción el evaluador es regenerado por el proceso de laconsola. Esto provoca que se pierdan tablas ETS entre otras cosas. Siejecutamos esta función con true el evaluador captura la excepcióny no muere.

i() / ni()

Muestra todos los procesos del nodo o de todos los nodosconectados respectivamente.

Page 178: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 178/189

La línea de comandos

162

6. Directorio de trabajoEn cualquier momento podemos modificar el directorio de trabajo dentrode la consola. Las siguientes funciones nos ayudan en esta y otras tareasrelacionadas:

cd(Dir)

Cambia el directorio de trabajo. Se indica una lista de caracteres conla ruta relativa o absoluta para el cambio.

ls() / ls(Dir)

Lista el directorio actual u otro indicado como parámetro de formarelativa o absoluta a través de una lista de caracteres.

pwd()

Imprime el directorio de trabajo actual.

7. Modo JCLCuando se presiona la combinación de teclas Control+G se accede auna nueva consola. Esta consola es denominada JCL (Job Control Mode omodo de control de trabajos). Este modo nos permite lanzar una nuevaconsola, conectarnos a una consola remota, detener una consola en

ejecución o cambiar de una a otra consola.

Importante

Cada trabajo que se lanza es una consola (shell). Este modo nospermite gestionar estas consolas. Cada nodo puede tener tantasconsolas como se quiera.

Estos son los comandos que podemos emplear en este modo:

c [nn]

Conectar a una consola. Si no se especifica un número vuelve alactual.

i [nn]

Detiene la consola actual o la que corresponda al número que seindique como argumento. Es útil cuando se quiere interrumpir unbucle infinito sin perder las variables empleadas.

k [nn]

Mata la consola actual o la que corresponda al número que seindique como argumento.

Page 179: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 179/189

La línea de comandos

163

 j

Lista las consolas en ejecución. La consola actual se indicará con un

asterisco (*).s [shell]

Inicia una consola nueva. Si se indica el nombre de un módulo comoargumento se intentará lanzar un proceso con ese módulo comoconsola alternativa.

r [node [shell]]

Indica que deseamos crear una consola en un nodo al que setiene conexión. Se lanza una consola en ese nodo y queda visibleen el listado de consolas. Se puede indicar también una consolaalternativa en caso de disponer de ella.

q

Finaliza la ejecución del nodo Erlang en el que estemos ejecutandoel modo JCL.

8. Salir de la consolaPara salir de la consola hay varias formas. Se puede salir presionandodos veces la combinación de teclas Control+C, ejecutando la función de

consola q() o a través del modo JCL y su comando q .

Page 180: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 180/189

164

Apéndice C. Herramientas

gráficasErlang es una máquina virtual además de un lenguaje por lo que requierede herramientas que le permitan gestionar sus procesos de una formafácil para el usuario. En el capítulo Capítulo5, Procesos tratamos la formaen la que listar los procesos de consola. Ahora veremos la forma de verestos procesos de forma gráfica, así como las tablas ETS y Mnesia y ladepuración de los procesos que ejecutemos.

1. Barra de herramientasPara facilitar la tarea de acceder al conjunto de herramientas gráficasdisponemos de toolbar . Esta aplicación nos proporciona una ventana conbotones de acceso directo a las herramientas de la interfaz gráfica deErlang.

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 (deizquierda a derecha):

tv

Table Visualizer o visor de tablas. Se emplea para poder visualizarel contenido de las tablas ETS y las que maneja la base de datos

Mnesia.pman

Process Manager o administrador de procesos. Es la versión gráficade lo que conseguimos con las funciones de consola i/0, i/1 yotras funciones como exit/1 integradas en una única interfaz.

debugger

El depurador nos permite seguir la ejecución de un código en laventana de proceso y revisar los datos de sus variables en esemomento.

Page 181: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 181/189

Herramientas gráficas

165

appmon

Application Monitor o monitor de aplicaciones. Permite ver la carga

que supone en el nodo la aplicación y el árbol de dependencia deprocesos entre otras opciones.

Nota

En los menús podemos ver opciones como Add GS contributions que agrega otros cuatro botones extra: el juego bonk, mandelbrotque es un generador de fractales, el juego othello y el juego Cols.

El código fuente de estas aplicaciones está disponible junto conel código fuente de Erlang, en el directorio:

otp_src_R15B02/lib/gs/contribs

2. Monitor de aplicaciones

El monitor de aplicaciones nos proporciona información sobre lasaplicaciones ejecutadas en la máquina virtual de Erlang. El concepto deaplicación proviene de OTP y se comentó en el Capítulo8, Ecosistema Erlang .

La aplicación al lanzarse genera un proceso principal que puede estarenlazado con otros. Esta relación de procesos es la que se monitorizabajo el nombre propio de cada aplicación que se ejecuta.

En el gráfico 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 muestranuna jerarquía de procesos. Cada proceso se identifica con su nombreregistrado o con su PID en caso de no disponer de nombre. Sobre cadaproceso podemos ejecutar una serie de acciones que se representan conlos botones superiores que se pueden ver en la ventana:

Page 182: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 182/189

Herramientas gráficas

166

La barra superior de botones es como una barra de herramientas.Siempre hay un botón que aparece como marcado indicando así laopción que se hará sobre cualquier proceso cuando se haga clic sobreél. Las acciones son:

Info

Se abrirá una nueva ventana que mostrará la información delproceso.

Send

Permite enviar un mensaje al proceso. Es equivalente a:

Pid ! Mensaje

Trace

Cada mensaje recibido al proceso marcado es impreso por laconsola. Es equivalente a la función erlang:trace/3.

Kill

Envía la señal de finalización al proceso. Equivalente a exit/1.

El menú de la ventana principal dispone además de otras opciones ensu menú Actions como: Reboot, Restart, Stop y Ping. Estas opciones sonútiles cuando se emplea la herramienta con otros nodos, ya que permitereiniciar estos nodos y cuando se vean como desconectados, hacerlesping para volver a conectarlos.

Page 183: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 183/189

Herramientas gráficas

167

Nota

El monitor de aplicaciones no solo puede visualizar el estado delas aplicaciones del nodo en el que fue lanzado, sino que tambiénpuede ver el estado de las aplicaciones de otros nodos a los quese encuentre conectado.

Esto es posible a través del menú Nodes , donde se listarán todoslos nodos a los que esté conectada la máquina virtual de Erlang.

3. Gestor de procesos

Una forma simple de gestionar los procesos que se ejecutan en la

máquina virtual de Erlang es a través de su gestor de procesos gráfico.El gestor de procesos nos permite visualizar de forma rápida y entiempo real los procesos que se están ejecutando en la máquina virtualde Erlang su PID, nombre registrado (si tienen), la función actual queestán ejecutando, los mensajes que tienen pendientes en el buźon, lasreducciones aplicadas y el tamaño que ocupa el proceso.

Esta interfaz permite activar el trazado de procesos en una ventanaaparte, visualizando en ella los mensajes entrantes al proceso que se estétrazando. Para trazar un proceso solo hay que seleccionarlo y a través del

menú Trace seleccionar la opción Trace selected process .

Nota

El gestor de procesos no solo puede visualizar y gestionar losprocesos del nodo en el que fue lanzado, sino que también puedever y gestionar los procesos de otros nodos a los que se encuentreconectado.

Esto es posible a través del menú Nodes , donde se listarán todoslos nodos a los que esté conectada la máquina virtual de Erlang.

Page 184: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 184/189

Herramientas gráficas

168

4. Visor de tablas

Las tablas ETS y las que se crean con la base de datos de Mnesia estándisponibles para su visualización a través de esta interfaz. Por defectonos muestra un listado de las tablas ETS que hay creadas en el nodo.La información que se muestra en la tabla principal es: nombre de latabla, identificador de la tabla, PID del propietario de la tabla, nombreregistrado del proceso propietario (si tiene) y tamaño de la tabla (ennúmero de entradas).

A través del menú View  se puede conmutar entre la visualización delas tablas ETS y las tablas Mnesia. En el menú Options  hay opcionesreferentes a la visualización de las tablas en el listado: refrescar, vertablas del sistema o no legibles son algunas de las opciones.

Si hacemos doble clic sobre cualquier entrada de la tabla principal, seabrirá una segunda ventana en la que se mostrará el contenido de la tablaETS seleccionada. Se visualiza como si fuese una hoja de cálculo y semarca sobre las columnas con el símbolo de una llave cuál es la claveprimaria de la tabla.

Esta es una ventana de visualización y edición por lo que podemosseleccionar las entradas de la tabla y editarlas o eliminarlas a través de

las opciones disponibles en el menú Edit .

Nota

El visualizador de tablas no solo puede visualizar y gestionar lastablas ETS y Mnesia del nodo en el que fue lanzado, sino quetambién puede hacer lo mismo con otros nodos a los que seencuentre conectado.

Esto es posible a través del menú File , opción Nodes..., dondese listarán todos los nodos a los que esté conectada la máquinavirtual de Erlang.

Page 185: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 185/189

Herramientas gráficas

169

5. Observer

El programaobserver 

es una unificación depman

,tv 

yappmon

ademásde otras características más en un entorno wxWindow mejorado conrespecto a los anteriores.

Podemos lanzar este programa de la siguiente manera:

> observer:start().

La ventana abierta dispone de un conjunto de pestañas o lengüetas quedisponen de todas las funcionalidades del conjunto de programas vistosen las secciones anteriores:

System

Ofrece información del sistema: versión y arquitectura de lamáquina virtual de Erlang, CPUs e hilos en ejecución, uso de lamemoria y estadísticas de uso.

Load Charts

Se presentan tres gráficos en tiempo real: utilización delprogramador de tareas (equivalente a la carga del procesador), usode la memoria y uso de la Entrada/Salida.

ApplicationsOfrece la misma información que appmon. Visualiza el listado deaplicaciones a la izquierda y el árbol de procesos a la derecha, previaselección de una de las aplicaciones.

Processes

Listado de procesos tal y como se presentaban también en pman.Se muestra una tabla con los procesos activos y su información:PID, nombre o función inicial, reducciones, memoria, mensajesencolados y función en ejecución actualmente.

Table Viewer

Lista las tablas ETS y Mnesia como lo hace la aplicación tv . Adiferencia de tv , observer no permite la creación de nuevas tablasETS aunque sí permite modificar y/o eliminar entradas de las tablasexistentes.

Trace Overview

Tanto la aplicación appmon como pman permiten trazar procesos.La aplicación observer unifica esto en una sola pestaña cambiandola forma en la que realizar las trazas de los procesos.

Page 186: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 186/189

Herramientas gráficas

170

La ventana tiene este aspecto:

Nota

Observer no solo puede actuar en el nodo en el que es lanzado,sino que también puede interactuar con otros nodos a los que seencuentre conectado.

Esto es posible a través del menú Nodes , donde se listarán todoslos nodos a los que esté conectada la máquina virtual de Erlangademás de dar la posibilidad de conectar con otros nodos que nose encuentren en el listado.

6. Depurador

Esta es quizás la herramienta más importante y que mejor se deberíade aprender a utilizar para poder analizar el código realizado de unaforma más cercana al dato. Aunque las trazas serán suficientes en algunoscasos, siempre es mejor depurar un código que nos origina un error queno 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:

Page 187: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 187/189

Herramientas gráficas

171

La forma más sencilla de emplear el depurador es a través del menúModule , opción Interpret... cargar un fichero de código fuente. En elpropio menú Module  debe de agregarse después una opción con elnombre del módulo cargado.

Tras esto, presionamos los tres cuadros de verificación visibles bajo elcuadro de la ventana donde aparece el nombre del módulo: First Call, OnBreak y On Exit . Con esto conseguimos que el depurador se active cuandose suceda cualquiera de estos tres eventos sobre el módulo.

En la consola de Erlang ejecutamos una función del módulo. Vemos quese abrirá otra ventana de depuración con el código en la parte principal,una botonera en la parte media con los botones: Next , Step, Finish, Where ,Up y Down.

Page 188: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 188/189

Herramientas gráficas

172

La parte inferior de la ventana muestra un listado de las variables delcontexto de la función que se está depurando a la derecha. En la parteizquierda hay un evaluador que permite escribir expresiones simples

para comprobación de datos.

Durante la ejecución la ventana principal mostrará la información de losprocesos que hay en ejecución, la llamada que los inició, el nombre delproceso, el estado del proceso (ejecución, ocioso u otro) e informaciónsobre la ejecución del proceso.

Nota

El depurador se puede lanzar en dos modos: local o global. Pordefecto se lanza de modo global, por lo que cualquier módulo que

se interprete será mostrado en la ventana del monitor.No es aconsejable lanzar el depurador en un cluster de más de dosnodos, ya que la ejecución concurrente de un mismo módulo envarios nodos al mismo tiempo podría llevar a un funcionamientoinconsistente.

Tomando ejemplos de los últimos capítulos podemos hacer pruebas deejecución del código, probar opciones de compilación, ver los procesos,la evaluación de expresiones, el contenido de las variables y otrosaspectos que nos ayuden a aprender a utilizar bien esta herramienta.

Para más información sobre la misma:

http://www.erlang.org/doc/apps/debugger/debugger_chapter.html

Page 189: erlang-i

7/30/2019 erlang-i

http://slidepdf.com/reader/full/erlang-i 189/189