erlang-i



Comments



Description

Erlang/OTPVolumen I: Un Mundo Concurrente Manuel Angel Rubio Jiménez Volumen I: Un Mundo Concurrente Manuel Angel Rubio Jiménez Resumen El lenguaje de programación Erlang nació sobre el año 1986 en los laboratorios Ericsson de la mano de Joe Armstrong. Es un lenguaje funcional con base en Prolog, tolerante a fallos, y orientado al trabajo en tiempo real y a la concurrencia, lo que le proporciona ciertas ventajas en lo que a la declaración de algoritmos se refiere. Como la mayoría de lenguajes funcionales Erlang requiere un análisis del problema y una forma de diseñar la solución diferente a como se haría en un lenguaje de programación imperativo. Sugiere una mejor y más eficiente forma de llevarlo a cabo. Se basa en una sintaxis más matemática que programática por lo que tiende más a la resolución de problemas que a la ordenación y ejecución de órdenes. Todo ello hace que Erlang sea un lenguaje muy apropiado para la programación de elementos de misión crítica, tanto a nivel de servidor como a nivel de escritorio, e incluso para el desarrollo de sistemas embebidos o incrustados. En este libro se recoge un compendio de información sobre lo que es el lenguaje, cómo cubre las necesidades para las que fue creado, cómo sacarle el máximo provecho a su forma de realizar las tareas y a su orientación a la concurrencia. Es un repaso desde el principio sobre cómo programar de una forma funcional y concurrente en un entorno distribuido y tolerante a fallos. Erlang/OTP Erlang/OTP, Volumen I: Un Mundo Concurrente por Manuel Ángel Rubio 1 Jiménez se encuentra bajo una Licencia Creative Commons Reconocimiento2 NoComercial-CompartirIgual 3.0 Unported . 1 2 http://erlang.bosqueviejo.net/ http://creativecommons.org/licenses/by-nc-sa/3.0/ Tabla de contenidos Prólogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . vii Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix 1. Acerca del autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix 2. Acerca de los Revisores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . x 3. Acerca del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi 4. Objetivo del libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xii 5. ¿A quién va dirigido este libro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii 6. Estructura de la colección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiii 7. Nomenclatura usada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xiv 8. Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xv 9. Más información en la web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvi 1. Lo que debes saber sobre Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1. ¿Qué es Erlang? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2. Características de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 3. Historia de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 4. Desarrollos con Erlang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4.1. Sector empresarial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 4.2. Software libre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 5. Erlang y la Concurrencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 5.1. El caso de Demonware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 5.2. Yaws contra Apache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2. El lenguaje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1. Tipos de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.1. Átomos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.2. Números Enteros y Reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.3. Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.4. Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 1.5. Tuplas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 1.6. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2. Imprimiendo por pantalla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3. Fechas y Horas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3. Expresiones, Estructuras y Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1. Expresiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1.1. Expresiones Aritméticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 1.2. Expresiones Lógicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 1.3. Precedencia de Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2. Estructuras de Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.1. Concordancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 2.2. Estructura case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.3. Estructura if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 2.4. Listas de Comprensión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3. Excepciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3.1. Recoger excepciones: catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 iii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 7. . . . . . . . . . . . . . . . . . . 64 2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . De ETS a DETS y viceversa . . . . . . . . . . . . . . . . . . . . . . 103 iv . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 13. . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 2.catch . . . . . . . . . . . . . . Procesos Remotos . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . Funciones Integradas . . . . . . . Bautizando Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 7. . . . . . . . . . . . . . . . . . Clausuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Abriendo y Cerrando Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Crear o abrir una DETS . . .6. . . . . . . . . . . . . . . . . . . . . . . . . Recarga de código . 75 8. . . . . 68 5. . . . . . . Ficheros . . 99 3. . . . . . 80 11. . . . . . . . . . . . . . . 44 3. . . . . . . . . . . . . . . Polimorfismo y Concordancia . . . . . . . .1. . . . 51 4. . . . . . Lectura y Escritura en ETS . . . . . . Lanzar una excepción . . . . . . . . . . . . . . . . . . Recursividad . . . . . . . . . . 91 1. . . . . . . . . . 48 2. . . . . . . 61 8. . . . . . . . . . . . . . . .. . . . 93 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 3. . . . . . . . . . . . . . . 80 10. 88 1. . . . . . . . . . . . . . . . Diccionario del Proceso . . . . . 87 1. . . . . . . . . . . . . . . . . . Las funciones y módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Acceso a las ETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nodos Erlang . . . . . . . 68 6. . . . . . . . . . . . . . . . . . . . . . Errores de ejecución más comunes . . . . . . . . . .2. Programación Funcional . . . . . . . . . . . . . . . . . . . . . . . 98 2. 101 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 7. . . . . . . . . . . . . . . . .2. . . . . . . . . DETS y Ficheros . Gestión de Procesos . . . . . . . . . Procesos . . . . . . . . . . . . . Tipos de Tablas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 1. . . . . . . . . . . . . . . . . . Procesos Locales o Globales . . 55 7. . Anatomía de un Proceso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 12. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comunicación entre Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ETS a fichero . . . . . . . . . . . . . . . . . . .7. . . . . . . . . . . . . . . Match: búsquedas avanzadas . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . 62 5. . . . . . . . . . . . . . . . . . . . Guardas . . . . . . . . . . . . . . . . . . 52 5. . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . Creación de una ETS . . 86 1. . . . . . . . 87 1. . . . . . . . . . . . . . . . . . . . . 48 1. . . . . . . . . . . . . . . . . . . . . . . 84 14. . . . . . . . . . Manipulación de las DETS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . .1. . . 85 6. . . . . . . . . . 67 4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ventajas e inconvenientes . . . . . . . . . . 77 9. . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La estructura try. . . . . . . . . . . . . . . . . . . . . . . . . Ordenación por mezcla (mergesort) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Organización del código . . . . . . . . . . . . . . . . . 65 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Erlang/OTP 3. . . . 43 3. . 94 2. . . . . . . . . . . . . . 52 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5. . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . Lectura de Ficheros de Texto . . . . . . . . . . Ordenación rápida (quicksort) . . . . . . . . . . . . . . . . . . . . . . . Escritura de Ficheros de Texto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lanzando Procesos . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . RPC: Llamada Remota a Proceso . . . 98 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Eliminando tuplas . . . . . . . . . . . . . . . . . . . DETS . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Procesos Enlazados . . . . . . . . . . . . . ETS . . . Monitorización de Procesos . . . . . . . . . . . . . . . 64 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ETS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 4. . . . . 99 3. . . . . Ámbito de las funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tipos de Tablas . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 1. . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ecosistema Erlang . . . . . . . . . . . . . . . . . . . Dependencias . . . . . . . . . . . . . . . . . Acceso aleatorio de Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A. . . . . . Guiones en Erlang . . . . . . . . . . . . . . 2. . .4. . . . . . . 3. . . . . . . . . . . . . . . 8. . .1. . 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. . . . . . . . . . . . . . . . . . . Otros sistemas . . . . . . . . . . . . . 5. 7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conceptos básicos de Redes . . . . . . . . . . . . . . . . . . . . . Gestión de Directorios . .7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3. . . . . . . . . 6. . . . . . . . . . . . . El camino a OTP . . . . . . . . . . . . . . . . . . . . Apéndices . . . . . . . . . . . . . . . . . . . . . . . Liberar y Desplegar . . . . . . . . . Comunicaciones . . . . . . . . . . 8. . . . . . . . . . . . . . . . . . . . 4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3. . . . . . . . . . Variables . 2. 7. . . . . . . . . . . . . . . . . 1. . . . . . . . . . . . Creación y Eliminación de Directorios . Gestión de Ficheros . . . . . . . . . . . . . Modo JCL . . B. . . . . . . . . . . . 4. . . . . . . . . . Desde Paquetes Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . Directorio de Trabajo . . . . Copiar. . . . . Creando y lanzando una aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. . . . . . . . . . . . Actualizando en Caliente . . . . . . . Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . Instalación en sistemas GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Puertos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Instalación en Windows . . . . . . . . . . Histórico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Servidor y Cliente UDP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. . 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . 3. . . . . . . La línea de comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5. . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. . . . . . . . 5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. . . . . . . . . . . . . . . . . . . . . .5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3. . . . . Compilando el Código Fuente . . . . . . . . . . . . . . . . . . . . . 1. . . . . . . . . . . . . . 5. . . Instalación de Erlang . . . . . . . . . . . . . . . . . . . . . . . . . Instalar rebar . . . . . Directorio de trabajo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4. . . . . . ¿Es un fichero? . . . . . . Registros . . . . . . . . . . . . . . . . . . . . . Procesos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ventajas de inet . . . . . . . . . . . . Permisos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . 2. . . . . . . . . . . . . . .1. . . . . . . . . . . 4. . 2. . . . . . . . . 7. . . . . 5. . . . . . . . . . . . . . . . . . . . . . . . 4. . . . . . Mover y Eliminar Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Erlang/OTP 3. . . . . . . . . . . . . . . . . . . . . . . 1. . 1. . . . . . . . . . . . . . . . . . . 5. . . . . . . . . . . . . . . . . . . . . . . . . . Compilar y Limpiar . . . . . . . . . . Direcciones IP . . . . . . . . . . Iniciar un Proyecto . . . . . . . . . . . . . . . . . 5. . . . . . . . . . 4. . . . . . . . . . . . . .6. . . . . . . . . . . . 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lectura de Ficheros Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . Contenido de los Directorios . . . . Propietarios y Grupos . . . . . . . . . . . . . . . . . . . Escritura de Ficheros Binarios . . 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . Servidor TCP Concurrente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Escribiendo el Código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Lecturas y Escrituras por Lotes . . . . . . . . . Servidor y Cliente TCP . . . . . . . . . . . . . . . . . Nombre del fichero . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 105 105 106 107 107 108 109 110 111 111 112 113 114 114 115 117 118 123 125 127 131 131 132 133 135 136 137 140 146 150 152 153 154 154 155 156 156 157 158 158 159 160 160 161 162 162 v . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Monitor de aplicaciones . . . . . . . . . . . . . . . . C. . . . . . . . . . . . . . . . . . . . . . . . . . . . Visor de tablas . . . . . . . . . . . . . . . . . Barra de herramientas . . . . . . . . . . . . . . . . . . . 3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 164 164 165 167 168 169 170 vi . . . . . . . 4. . . . . . . . . . . . . . . . . . . . . Depurador . . . . . . .Erlang/OTP 8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Salir de la consola . . . 6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Gestor de procesos . . . . . . . . . . . . . . . . . . Herramientas gráficas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Observer . . . . . . . . . . . . . . . . . . . . . . . . . 5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . enseguida me di cuenta de la similitud de nuestras ideas y objetivos: escribir programas eficientes y escalables. te permiten diseñar y desarrollar desde el inicio sistemas robustos. Con el paso de los días empecé a comprender que el salto que estaba realizando no era como aprender otro lenguaje más (saltar entre PHP. 2. La introducción al lenguaje propuesta por Manuel Angel. En un mundo donde predomina la programación imperativa. y terminando con las funcionalidades nativas que lo diferencian. desde la introducción hasta las reseñas. Cuando alguien me comenta que quiere aprender Erlang suelo decir tres cosas: 1.. ayudarán tanto a lectores noveles. estaba aprendiendo una nueva forma de pensar y resolver problemas la esencia de los lenguajes funcionales. simplemente con ver su dilatada experiencia en un abanico tan amplio de tecnologías. ha provocado que Erlang sea relevante dos décadas después de su creación. Y aunque no lo conocía personalmente.Prólogo Conocí a Manuel Angel cuando me explicó su idea de escribir un libro sobre Erlang en Castellano. Lee un buen libro. pasando por los tipos de datos y expresiones.. desde la base.). . Tras intercambiar algunos emails. como a lectores con experiencia en programación funcional. optimización de la máquina virtual. despliegues en producción sin interrupciones. La necesidad de sistemas que sean capaces de gestionar millones de usuarios concurrentes de manera eficiente. el primer instinto fue extrapolarlos a aquel primer reto al que me enfrentaba (iterar sobre una lista). evitando tener que resolver problemas de escalabilidad y operaciones complejas en las siguientes fases de un proyecto (capas de cache complejas. teniendo en cuenta que todos los conocimientos que tenía estaban basados en lenguajes orientados a objetos. Mi primera experiencia con Erlang fue como ver Matrix. ya intuí que el material que saldría de su cabeza sería de ayuda para todo tipo lectores. completo. no sólo me pareció una idea apasionante. vii . que los conceptos y herramientas que proporciona de manera nativa Erlang. Intenta con todas tus fuerzas olvidar todo lo que sepas de programación imperativa. Cabe destacar. sino un hito imprescindible para llevar este lenguaje de programación a donde se merece entre la comunidad castellano-parlante. Java o Ruby). los lenguajes funcionales vuelven a cobrar importancia por su potencia y sencillez. con un estilo claro y directo. ese consejo estaba muy condicionado al conocimiento de inglés de la persona que lo recibía. Gracias a este libro. y si sumamos todos los nuevos conceptos al que el lector se enfrenta. ejemplos útiles y el reflejo de la experiencia del autor. el resultado no siempre era el esperado. de la que espero nazcan desde simples algoritmos rápidos y eficientes. hasta sistemas distribuidos altamente escalables. hará que aprender este lenguaje sea una experiencia productiva. —José Luis Gordo Romero viii . Hasta el momento de la publicación de este libro.Prólogo 3. Ten siempre una consola a mano para ir poniendo en práctica los conocimientos adquiridos. Python. En 2008 volví a retomar el desarrollo de sistemas del área de voz. —Louis Pasteur 1. Pascal y Modula-2 entre otros. Acerca del autor La programación es un tema que me ha fascinado desde siempre. en 2003. comenzando así una aventura que me ha llevado al aprendizaje de nuevos lenguajes de programación regularmente. principalmente en telefonía. En aquella época. El lenguaje C++ me abrió las puertas de la orientación a objetos y ese mismo año ya comencé a interesarme por Java. Perl. Java. siempre con el interés de analizar sus potencias y debilidades. En Erlang encontré un mundo en el que es posible desarrollar estructuras complejas cliente-servidor. Por si fuera poco. el desarrollo mediante lenguajes imperativos. en unos tiempos y con una calidad y robustez que parecería ix . me centré en perfeccionar mis conocimientos sobre C++. Con el bagaje de la experiencia anterior y dispuestos a aplicar las mejores soluciones que proporciona Erlang. hicieron que la creación de soluciones fuese excesivamente costosa y se terminase desechando. a la edad de 22 años. Perl y PHP.Introducción Sorprendernos por algo es el primer paso de la mente hacia el descubrimiento. el paradigma de la orientación a objetos y sus particularidades de implementación en este lenguaje. En los siguientes 8 años. Así es como experimenté también con lenguajes clásicos como Basic. Ruby. y las estructuras propias de concurrencia y distribución. Pascal. descubrí Erlang. tanto estructurados como orientados a objetos. aprendí SQL. después de haber tratado con lenguajes imperativos. Al año siguiente. En el año 2006. A partir del año 2002. elegante y fácilmente comprensible. Estos proyectos los desarrollamos con éxito con una escasa cantidad de código. PHP. estas estructuras se crean mediante un código compacto y con una sintaxis clara. encabecé una serie de proyectos para entornos de telecomunicaciones. en el que los procesos son concurrentes. con sus particularidades y ecosistemas como son C/C++. robustos y tolerantes a fallos. encabecé algunos desarrollos en una oficina de I+D en Córdoba que no resultaron del todo favorables. distribuidos. Modula-2 y otro tipo de lenguajes de scripting para la gestión de sistemas informáticos como Perl o lenguajes de shell. Trabajar en startups me ha permitido explorar y profundizar en diferentes tecnologías. José Luis Gordo Romero Apasionado de la tecnología y del software libre. muy orientado al framework OTP y la metodología que propone. Puedo lanzarme a esta tarea no sin antes recomendar la literatura existente que me ha servido como referencia durante mi propio proceso de aprendizaje y paso a enumerar.Introducción imposible en otros lenguajes. Juan Sebastián Pérez Herrero Soy experto en diversas tecnologías. El de Francesco Cesarini. sin las cuales. además de poder colaborar en varios proyectos de software libre (de los cuales disfruto aprendiendo y aportando todo lo que puedo). En esta sección hacemos una pequeña presentación de ambos. de seguro que sería mucho más complejo de seguir. me he dado cuenta de que hace falta llenar el hueco que deja el no tener literatura sobre Erlang en nuestro idioma. con abultada experiencia en entornos web. completísimo y centrado en el lenguaje base que he releído decenas de veces. integración y gestión 1 http://erlware. Acerca de los Revisores Este libro ha sido revisado por dos personas. efectividad y la capacidad de escalado que brinda el lenguaje y su máquina virtual. y de paso tratar los temas desde otro punto de vista.com/ x . Con todo lo aprendido y hecho en torno a este lenguaje. ha sido todo un placer. Igualmente comprobamos la simpleza. Durante mi carrera profesional he recorrido distintas áreas tecnológicas. igualmente recomendable. plataformas de movilidad. Actualmente estoy centrado en varios proyectos donde Erlang es la base. Empecé por la administración y automatización de sistemas. lo que me ha permitido afrontar proyectos teniendo una perspectiva global. El libro de Joe Armstrong sobre Erlang. pasando por el desarrollo hasta llegar al diseño de arquitectura (en entornos web). aunque más orientado al desarrollo de proyectos en Erlang. así que haber podido ayudar en la revisión y escribir el prólogo. 2. Incluso otro gran libro 1 que he conocido recientemente del equipo que mantiene Erlware . probablemente mejorable. y mucho menos en castellano. Con ello pudimos observar las capacidades de este lenguaje y lo bien que se adaptaba a nuestras necesidades de rendimiento. Ponerse en la piel de un lector y facilitarle las cosas sin bajar demasiado el nivel técnico es un reto. su carácter templado hace que siempre se pueda llegar a acuerdos y sus indicaciones. lo que ha servido para un enriquecimiento mutuo. Espero que el resultado. 3. Es complejo adentrarse en un lenguaje nuevo que nada tiene que ver con lenguajes con los que se haya trabajado anteriormente (salvo excepciones como Lisp.Introducción de proyectos internacionales y entornos open source de lo más diversos. facilite la lectura y comprensión de la obra y por ende su difusión. por lo que me decidí a escribir xi . sobre todo si se busca la robustez que normalmente requieren proyectos críticos que en muchas ocasiones manejan transacciones monetarias. He realizado la edición de varios capítulos y ha resultado ser una experiencia más que interesante. la robustez y la agilidad en los desarrollos fuera la necesaria para este tipo de sistemas. Buscábamos que los tiempos de respuesta. Ya estoy deseando leer la segunda parte sobre OTP. Scheme o Prolog). estuvimos trabajando en proyectos de fuerte concurrencia para el tratamiento de llamadas telefónicas. era una oportunidad de divertirme haciendo una tarea nueva como es la edición de literatura técnica en español que no estaba dispuesto a dejar pasar. Tras haber empleado diversas herramientas y técnicas de replicación y compartición en base de datos sin obtener resultados totalmente satisfactorios nos decidimos a introducir Erlang. Acerca del libro Durante el año 2008. He sido compañero de trabajo de Manuel. Me gusta participar en proyectos estimulantes y la edición de este libro junto con el aprendizaje de Erlang. especialmente en el procesamiento a tiempo real y de sistemas con alto número de transacciones me han sido de gran interés. Fue entonces cuando nos dimos cuenta de que había muy poca cantidad de información acerca del lenguaje (aunque poco a poco se vaya subsanando). El presente libro permite formarse en programación concurrente en Erlang de una forma entretenida. Su amplio espectro de conocimiento aporta muchos patrones y antipatrones. ya que intuyo que para proyectos de cierta envergadura se requieren unas directrices claras y el uso de buenos patrones de diseño. tanto en conocimientos como en estrategia. el desarrollo inicial. hay que tener siempre en mente que un lenguaje puede estar orientado a resolver un problema determinado más adecuadamente que otros. • Conocer las fortalezas y debilidades del lenguaje. En este texto analizamos qué es Erlang y en qué se puede emplear. puede ocurrir que su sintaxis sea paradójicamente más fácil para el que no sabe programar que para desarrolladores avanzados de lenguajes como C. El nivel y densidad de ciertas explicaciones son más bajos cuando se tratan en el idioma nativo. Por último. Aunque cada año haya un nuevo lenguaje que ofrece versatilidad y gran cantidad de facilidades. tolerantes a fallos y que deben prestar un servicio continuo en la red de redes. librerías o frameworks. Java o PHP. lo que hace que sea más fácil de entender.Introducción el libro que me hubiese gustado encontrar. sin duda. con lo que se obtendrá una idea clara de posibles casos de uso cuando tenga que acometer un nuevo desarrollo. Como en el uso de cualquier tecnología es importante tener la capacidad de seleccionar un lenguaje o entorno frente a otro dependiendo del trabajo que se vaya a realizar. Ya que Erlang no es un lenguaje imperativo. Hay desarrollos que en una versión temprana se han abandonado completamente y se han recomenzado de otra forma. el presente libro proporciona el conocimiento de lo que es Erlang. y lo que significan estas xii . Para ampliar el conocimiento y la posibilidad de elección. La motivación a la hora de seleccionar una tecnología no puede ser nunca una moda o la inercia. Hay muchos casos en los que una mala elección tecnológica ha forzado a reescribir. Objetivo del libro Con este libro pretendo cubrir principalmente los aspectos más importantes dentro del ámbito de aprendizaje de un nuevo lenguaje de programación: • Explicar los aspectos básicos del lenguaje para comenzar a programar. lo que es OTP. 4. Ya sea con otros lenguajes. En este punto hay que ser mucho más pragmáticos que fanáticos. el hecho de que el texto esté en castellano hace que. herramientas. sobre todo ahora que se incrementa el número de sistemas concurrentes. Un libro con las palabras justas y los diagramas apropiados para poder entender más rápidamente todos los conceptos nuevos que se ponen delante del programador de Erlang y OTP. sea más asequible para el público de habla hispana. de alta disponibilidad. versión a versión. El hecho de toparse con impedimentos tan grandes de salvar ha provocado que una reescritura desde cero sea con frecuencia lo más simple y rápido. Cada volumen tiene como misión explorar Erlang de una forma diferente. Permite realizar un recorrido por las potencias del lenguaje y obtener el conocimiento de sus debilidades. en el que se pueden desarrollar un cierto abanico de soluciones de forma rápida y segura. analista o arquitecto. Para los programadores neófitos ofrece una guía de aprendizaje base. por tanto. Permite igualmente ampliar el vocabulario de programación del lector con nuevas ideas sobre el desarrollo de programas y la resolución de problemas. o bien relativamente similar a otros vistos (si se tienen conocimientos de Lisp. un lenguaje y un entorno. 5. y con un objetivo diferente. Erlang es un lenguaje con muchos años de desarrollo. Propone ejemplos. Incluso si ya se conoce Erlang supone un recorrido por lo que ya se sabe. resolver y responder. desde un punto de vista diferente. ejercicios y preguntas que el programador puede realizar. La colección. ofrece el punto de vista de una herramienta. consta de dos volúmenes. Provee un acercamiento detallado a las entrañas de un sistema desarrollado con una ideología concreta y para un fin concreto. Prolog o Haskell). 6. y a aquellos que quieren saber qué puede hacer este lenguaje para tomarlo en consideración en las decisiones tecnológicas de su empresa o proyecto.Introducción nuevas herramientas que va ganando cada vez más relevancia en los entornos mencionados. Scheme. ¿A quién va dirigido este libro? Este libro está dirigido a todo aquél que quiera aprender a programar en un lenguaje funcional con control de concurrencia y distribuido. una forma rápida de adentrarse en el conocimiento del lenguaje que permite comenzar a desarrollar directamente. Esto se aplica tanto a los que comienzan a programar como a los que ya saben programar. Los volúmenes son: xiii . si el lector proviene del mundo imperativo. probado en producción por muchas empresas conocidas y desconocidas. Para el desarrollador. pero viendo el tamaño que estaba alcanzando pensé que mejor era dividirlo por temática y darle a cada libro la extensión apropiada como para ser leído y consultado de forma fácil y rápida. pero desde otro enfoque y con características o detalles que probablemente no se conozcan. Lo suficiente como para saber si es una buena herramienta para desarrollar una solución específica. Estructura de la colección Al principio pensé en escribir un único libro orientado a Erlang. Para el programador experimentado ofrece un nexo hacia un lenguaje diferente. se tenga a mano un ordenador con Erlang instalado. Sus formatos son los siguientes: Nota Esta es la forma que tendrán las notas informativas. Importante Estas son las notas importantes que indican usos específicos y detalles importantes que hay que tener muy en cuenta. Tendrán este aspecto: -module(hola). Además de ejemplos con código Erlang. en los distintos apartados del libro hay diferentes bloques que contienen notas informativas o avisos importantes. los supervisores y manejadores de eventos. ya que detalla toda la estructura del lenguaje en sí. sus estructuras. las máquinas de estados. el framework actualmente más potente para Erlang y que viene con su instalación base. el uso de los ficheros y comunicaciones a través de la red. los tipos de datos. así como la compilación de los ejemplos y su ejecución de forma básica. Los códigos aparecen de una forma visible y con un formato distinto al del resto del texto. Se verán los generadores de servidores. mundo() -> io:format("Hola mundo!~n". Nos adentramos en el conocimiento del sistema OTP. Nomenclatura usada A lo largo del libro encontrarás muchos ejemplos y fragmentos de código. Contienen detalles o información adicional sobre el texto para satisfacer la curiosidad del lector. así como acceso a su consola y un directorio en el que poder ir escribiendo los programas de ejemplo. xiv .Introducción • Un mundo concurrente. []). Será el bloque más extenso. Se recomienda su lectura. Nota Recomiendo que. sus elementos más comunes. En esta parte nos centraremos en conocer la sintaxis del lenguaje. 7. En este caso será de bastante ayuda revisar los apéndices donde explica cómo se descarga. para poder hacer los ejemplos y practicar lo que se va leyendo. • Las bases de OTP. entre otros elementos. instala y usa la consola de Erlang. Juan Antonio y Ana María. así como aprender de cada situación. así como el logotipo de BosqueViejo. A otros compañeros en lo profesional y personal. me ha dado la oportunidad de conocer mejor su trabajo y a él personalmente. Además de ampliar conocimientos. A Juan Sebastián Pérez. Juan Sebastián Pérez Gracias a Manuel por compartir tantos cafés (descafeinados) e ideas. Por último pero no por ello menos importante. Jonathan Márquez. Agradecimientos Manuel Ángel Rubio Agradecer a mi familia. así como su amor y cariño. amigos y pareja que me han xv . Y como no. así como a competir conmigo mismo para aprender y superarme en cada reto personal y profesional. si volvemos a coincidir. Marga. También agradecer a José Luis Gordo por su revisión. agradecer a mi hermano Rafael y a Luz (Bethany Neumann) el diseño de la portada y contraportada del libro. de cada lenguaje y de cada herramienta. especialmente al departamento de movilidad de Jet Multimedia por compartir fatigas y éxitos. por brindarse también a aprender el lenguaje de manos de este manuscrito. Margarita Ortiz y Daniel López. Aprendí mucho con ellos y espero que podamos seguir aprendiendo allá donde nos toque estar y. Respecto al libro. así como corregir también mi forma de expresarme en algunos puntos que confieso fueron complicados. sin la cual este libro nunca hubiera visto la luz. muchísimo mejor. el prólogo escrito y sus buenos consejos así como su crítica constructiva. a mi familia. ha sido un aliado inestimable en esta aventura y un balón de oxígeno en momentos arduos. A mis padres por enseñarme a defenderme en esta vida. José Luis Gordo Agradecer a Manuel Angel su confianza por haberme dejado aportar mi pequeño granito de arena a este proyecto. el que cada desarrollo que nos planteasen pudiésemos verlo como un desafío a nosotros mismos y sacar lo mejor de nosotros mismos. María Luisa de la Serna.Introducción 8. he de agradecer al equipo con el que estuve trabajando en Jet Multimedia: Guillermo Rodríguez. descubriendo su increíble energía y motivación. por ser pacientes y dejarme el tiempo suficiente para escribir. El sitio web: http://erlang. fe de erratas y comentarios. contactos.Introducción apoyado en el desarrollo de mis habilidades en otros aspectos de la vida. 9. ayuda y demás sobre el libro Erlang/OTP he habilitado una sección en mi web. Más información en la web Para obtener información sobre las siguientes ediciones.net xvi .bosqueviejo. debemos entender que se trata de un entorno o plataforma de desarrollo completa. Erlang proporciona no sólo el compilador para poder ejecutar el código. En este capítulo introducimos el concepto de Erlang y OTP. características e historia. Donde encaja mejor Erlang. paralela o concurrente y además con mecanismos para la tolerancia a fallos. —Joe Armstrong Erlang comienza a ser un entorno y un lenguaje de moda. aunque no completa. Fue diseñado desde un inicio para ejecutarse de forma ininterrumpida. es como un lenguaje orientado a la concurrencia. Lo que debes saber sobre Erlang Software para un mundo concurrente. Más adelante explicaremos cómo funciona esto concretamente. En principio.Capítulo 1. de tipo imperativo. al tener elementos de tipo funcional. La información de este primer capítulo se completa con las fuentes que lo han motivado y se provee información precisa sobre dónde se ha extraído cada sección. Esto significa que se puede cambiar el código de sus aplicaciones sin detener su ejecución. La existencia creciente de empresas orientadas a la prestación de servicios por internet con un elevado volumen de transacciones (como videojuegos en red o sistemas de mensajería móvil y chat) hace que en sitios como los Estados Unidos. e incluso algunos rasgos que permiten cierta orientación a objetos. Reino Unido o Suecia proliferen las ofertas de trabajo que solicitan profesionales en este lenguaje. sino que posee también una colección de herramientas. 1 . está entendido que sí lo es. Erlang tiene una gran facilidad para la programación distribuida. Su significado. Existe una necesidad imperiosa de desarrollar entornos con las características de la máquina de Erlang. Por ello Erlang podría mejor catalogarse como un lenguaje híbrido. por lo tanto existen dos enfoques: Erlang como lenguaje Hay muchas discusiones concernientes a si Erlang es o no un lenguaje funcional. ¿Qué es Erlang? Para comprender qué es Erlang. y la metodología de desarrollo proporcionada por OTP. aunque tenga elementos que le hagan salirse de la definición pura. 1. y una máquina virtual sobre la que ejecutarlo. al menos desde mi punto de vista. que le proporciona todas las características de distribución y comunicación de procesos. aunque no sólo por él. es también una máquina 1 que interpreta un pseudocódigo máquina que nada tiene que ver. como pueden ser: Reia. con el lenguaje Erlang.Lo que debes saber sobre Erlang Erlang como entorno de ejecución Como hemos mencionado antes Erlang es una plataforma de desarrollo que proporciona no sólo un compilador. 1 O trozos de código nativo si se emplea HiPE. vieron que el desarrollo de aplicaciones basadas en PLEX no era del todo óptimo para la programación de aplicaciones dentro de los sistemas hardware de Ericsson. Se buscaba un sistema que pudiera lanzar procesos no sólo en la máquina en la que se ejecuta. Según Bjarne Däcker. sino que también fuera capaz de hacerlo en otras máquinas. a ese nivel. Efene. esta dualidad es intencionada. Fue creado inicialmente por Ericsson. jefe del Computer Science Lab en su día. más específicamente por Joe Armstrong. Joxa o LFE. La máquina virtual sobre la que se ejecuta el código pseudocompilado de Erlang. Erlang se pseudocompila y su máquina virtual le proporciona una importante capa de abstracción que le dota de la capacidad de manejar y distribuir procesos entre nodos de forma totalmente transparente (sin el uso de librerías específicas). debido a su uso intensivo en Ericsson. 2. 2 . Esto ha permitido la proliferación de los lenguajes que emplean la máquina virtual pero no el lenguaje en sí. Recibe el nombre de Agnus Kraup Erlang. Erlang fue propietario hasta 1998 momento en que fue cedido como código abierto (open source) a la comunidad. Lo que en lenguajes como C viene a ser PVM o MPICH pero sin el uso explícito de ninguna librería. sino también una máquina virtual para su ejecución. A veces se piensa que el nombre es una abreviación de ERicsson LANGuage. Características de Erlang Durante el período en el que Joe Armstrong y sus compañeros estuvieron en los laboratorios de Ericsson. Perl. PHP o Ruby. Por esta razón comenzaron 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 carga entre los sistemas hardware. A diferencia de otros lenguajes interpretados como Python. Elixir. que el sistema no se detenga nunca. Hay otros lenguajes como Java. Cambiar el código en caliente También es importante en el entorno de Ericsson. a su vez con sus propios procesos. en los entornos con memoria compartida. aunque haya que realizar actualizaciones. que vaya controlando los estados de la misma y pueda provocar eventos hacia un manejador. Lenguaje simple Para rebajar la curva de aprendizaje el lenguaje debe de tener pocos elementos y ninguna excepción. Escalable Los sistemas operativos convencionales tenían problemas en mantener un elevado número de procesos en ejecución. Aspectos tan significativos como: Asignaciones únicas Como en los enunciados matemáticos la asignación de un valor a una variable se hace una única vez y. sin necesidad de parar el sistema y sin que afectase al código en ejecución. 3 .Lo que debes saber sobre Erlang Tolerante a fallos Si una parte del sistema tiene fallos y tiene que detenerse. Por lo que se buscaba un sistema que pudiese gestionar desde cientos de miles. afectando sólo a una parte del programa y no a todos sus hilos. hasta millones de procesos. durante el resto del enunciado. Erlang es un lenguaje simple de comprender y aprender. También había aspectos íntimos del diseño del lenguaje que se quisieron tener en cuenta para evitar otro tipo de problemas. un error puede dejar corrupta esta memoria por lo que esa opción no garantiza tampoco que no afecte al resto del programa. carece de bucles y emplea técnicas como la recursividad y modularización para conseguir algoritmos pequeños y eficientes. En sistemas software como PLEX o C. y en la mayoría de sistemas críticos o sistemas en producción de cualquier índole. esta variable mantiene su valor inmutable. Los sistemas de telefonía que desarrolla Ericsson se basan en tener un proceso por cada llamada entrante. ya que tiene nada más que dos estructuras de control. Python o Ruby que manejan estos errores como excepciones. No obstante. Por ello se agregó también como característica el hecho de que el código pudiese cambiar en caliente. un fallo en el código determina una interrupción completa del programa con todos sus hilos y procesos. Esto nos garantiza un mejor seguimiento del código y una mejor detección de errores. que esto no signifique que todo el sistema se detenga. Hace no mucho encontré una presentación bastante interesante sobre 2 Erlang . al igual que en lenguajes como Prolog o Lisp. Paso de mensajes en lugar de memoria compartida Uno de los problemas de la programación concurrente es la ejecución de secciones críticas de código para acceso a porciones de memoria compartida. tenga que solicitárselo al proceso en cuestión. Los lenguajes de propósito general serán óptimos para el desarrollo general de ese software de gestión empresarial. este lenguaje se orienta a la concurrencia de manera que las rutinas más íntimas del propio lenguaje están preparadas para facilitar la realización de programas concurrentes y distribuidos. Como es obvio. En sí. Orientado a la Concurrencia Como una especie de nueva forma de programar. presentan el problema de carecer de elementos a bajo nivel integrados en sus 2 http://www. El paso de mensajes hace que un proceso sea el responsable de los datos y la sección crítica se encuentre sólo en este proceso. el porqué no lo pudo encontrar en otros lenguajes. En principio hay que entender que propósito general se refiere al uso generalizado de un lenguaje a lo más cotidiano que se suele desarrollar. es un fabuloso lenguaje de marcas que facilita bastante la tarea a los desarrolladores web y sobre todo a maquetadores que se meten en el terreno de la programación. PHP por ejemplo. Esto abstrae al máximo la tarea de desarrollar programas concurrentes. sino también la contraposición. de modo que cualquiera que pida ejecutar algo de esa sección crítica. en la que se agregaba.se/edu/course/homepage/projektDV/ht05/uppsala. Erlang/OTP se basa en el paso de mensajes en lugar de emplear técnicas como semáforos o monitores. simplificando enormemente los esquemas y eliminando la necesidad del bloque explícito. es más frecuente hacer un software para administración de una empresa que un sistema operativo. Pero es algo completamente desastroso para el desarrollo de aplicaciones de scripting para administradores de sistemas. no sólo todo lo que comentaba Armstrong que debía de tener su sistema para poder desarrollar las soluciones de forma óptima. los lenguajes más difundidos hoy en día. Para simplificar e intentar eliminar el máximo posible de errores. como C# o Java. seguramente no tanto para ese software del sistema operativo.Lo que debes saber sobre Erlang Las estructuras de datos se simplifican también bastante y su potencia.pdf 4 .uu. Este control de acceso acaba siendo un cuello de botella ineludible.it. se basa en las listas. los requisitos que se buscaban cumplir eran: 3 http://www. 3. si se puede resolver un problema a través de una serie de ecuaciones matemáticas y portar ese mismo esquema a un programa de forma que el esquema funcional se respete y entienda tal y como se formuló fuera del entorno computacional. desarrollaron una lógica concurrente de programación para canales de comunicación.pdf 5 . Esta álgebra de telefonía permitía a través de su notación describir el sistema público de telefonía (POTS) en tan sólo quince reglas. Historia de Erlang Joe Armstrong asistió a la conferencia de Erlang Factory de Londres. A través del interés de llevar esta teoría a la práctica desarrollaron modelos en Ada. ya que algorítmicamente es más fácil de probarlo con las reglas propias de las matemáticas que computacionalmente con la cantidad de combinaciones que pueda tener. con un número reducido de instrucciones que rápidamente fue creciendo gracias a su buena acogida. CLU. con lo que comenzaron a desarrollar un sistema determinista en él. Sirviéndome de las diapositivas que proporcionó para el evento. puede ser fácil de tratar por la gente que entiende el esquema. donde explicó la historia de la máquina virtual de Erlang. que estaba siendo desarrollada en PLEX. basándose en las ventajas que habían visto de Prolog para conformar su base. un lenguaje propietario.erlang-factory. Joe Armstrong junto a dos colegas. en Prolog. La conclusión a la que llegó el equipo fue que. Así descubrieron que el álgebra telefónica se procesaba de forma muy rápida en sistemas de alto nivel. Esta es la razón de que en el mundo Java comience a hacerse cada vez más visible un lenguaje como Scala. La idea de Erlang surgió por la necesidad de Ericsson de acotar un problema que había surgido en su plataforma AXE.com/upload/presentations/247/erlang_vm_1. Smalltalk y Prolog entre otros. Básicamente. incluso mejorarlo y adaptarlo. Elshiewy y Robert Virding. En sí. vamos a dar un repaso a la historia de Erlang/ OTP. Prolog no era un lenguaje pensado para concurrencia. Las pruebas realmente se realizan a nivel teórico sobre el propio esquema. en 2010.Lo que debes saber sobre Erlang sistemas que les permitan desarrollar aplicaciones concurrentes de forma fácil. 3 es la propia historia de Erlang/OTP. Erlang vió la luz en 1986. por lo que se decidieron a realizar uno que satisfaciera todos sus requisitos. después de que Joe Armstrong se encerrase a desarrollar la idea base como intérprete sobre Prolog. es decir. pero surgió el problema de que su rendimiento no era el adecuado. pero tenía varios problemas para que pudiera ser adoptado de forma amplia por la comunidad de programadores. al menos unas 40 veces más rápido. sino que se recargue en caliente. pero tendría que ser. Mike Williams se encargó de escribir el emulador. el sistema estaba comenzando a dar sus frutos. Con el tiempo. sintaxis de bit o compilación de patrones para matching. HiPE. lo que obliga a que para actualizar el código del sistema no se deba detener su ejecución. por su parte Robert Virding se encargaba de escribir las librerías. estructura OTP. el heap de memoria y la pila. la imposición de no escribir código en Erlang se fue olvidando y la comunidad de programadores de Erlang comenzó a crecer fuera de Ericsson. se le potenció agregando elementos como distribución. cargador. • Debía poder ejecutar desde miles a millones de procesos concurrentes y cada proceso ser independiente del resto. de modo que si alguno de ellos se corrompiese no dañase el espacio de memoria de otro proceso. En los años 90. En 1989. Desafortunadamente para el desarrollo de Erlang. planificador y recolector de basura (en lenguaje C) mientras que Joe Armstrong escribía el compilador. no una librería o framework de desarrollo. tras haber conseguido desarrollar productos de la gama AXE con este lenguaje. Nota HiPE es el acrónimo de High Performance Erlang (Erlang de Alto Rendimiento) que es el nombre de un grupo de investigación sobre Erlang formado en la Universidad de Uppsala en 1998. El equipo OTP se mantuvo desarrollando y soportando 6 . Erlang comenzaba a ser una gran pieza de software. Este requisito nos lleva a que el fallo de los procesos debe de ser aislado del resto del programa. las estructuras de datos. El grupo desarrolló un compilador de código nativo de modo que la máquina (BEAM) virtual de Erlang no tenga que interpretar ciertas partes del código si ya están en lenguaje máquina mejorando así su rendimiento. El sistema desarrollado se optimizó a un nivel en el que consiguieron aumentar su rendimiento en 120 veces de lo que lo hacía el intérprete en Prolog. • Debe poder ejecutarse de modo ininterrumpido.Lo que debes saber sobre Erlang • Los procesos debían de ser una parte intrínseca del lenguaje. Se llegó a la conclusión de que el lenguaje era adecuado para la programación que se realizaba. aquél periodo fue también la década de Java y Ericsson decidió centrarse en lenguajes usados globalmente por lo que prohibió seguir desarrollando en Erlang. Lo que debes saber sobre Erlang Erlang que.erlang-factory. Esto es un punto esencial y decisivo para empresas que tienen su nicho de negocio en Internet y que han pasado de vender productos a proveer servicios a través de la red.1. cuyo uso pueden verse en empresas como la española Tractis. Cada vez más sectores se hacen eco de las capacidades de Erlang y cada vez más empresas han comenzado desarrollos en esta plataforma por lo que se augura que el uso de este lenguaje siga al alza. Al igual que Tuenti que también emplea esta tecnología.slideshare. 4 5 4 http://www. Antes de 2010 Erlang agregó capacidad para SMP y más recientemente para multi-core. Merecen mención elementos tan necesarios como ChicagoBoss o Nitrogen. a su vez. continuó como sufragador del proyecto HiPE y aplicaciones como EDoc o Dialyzer. Varias empresas del sector del entretenimiento que fabrican aplicaciones móviles también se han sumado a desarrollar sus aplicaciones de parte servidora en Erlang/OTP.com/conference/London2011/speakers/MalcolmDowse http://es.net/wooga/erlang-the-big-switch-in-social-games 7 . Un ejemplo de este tipo 5 de empresas es Wooga . Desarrollos con Erlang Los desarrollos en Erlang cada vez son más visibles para todos sobre todo en el entorno en el que Erlang se mueve: la concurrencia y la gestión masiva de eventos o elementos sin saturarse ni caer. También es conocido el caso de Facebook que emplea Erlang en su implementación de chat para soportar los mensajes de sus 70 millones de usuarios. por lo que es 36.000 veces más rápido que el original interpretado en Prolog. La revisión de 2010 del emulador de BEAM se ejecuta con un rendimiento 300 veces superior al de la versión del emulador en C. La empresa inglesa Demonware . comenzó a emplear Erlang para poder soportar el número de jugadores de títulos tan afamados como Call of Duty. 4. especializada en el desarrollo y mantenimiento de infraestructura y aplicaciones servidoras para videojuegos en Internet. 4. Sector empresarial Debido a las ventajas intrínsecas del lenguaje y su entorno se ha hecho patente la creación de modelos reales MVC para desarrollo web. En esta sección veremos la influencia de Erlang y cómo se va asentando en el entorno empresarial y en las comunidades de software libre y el tipo de implementaciones que se realizan en uno y otro ámbito. Una de las empresas estandarte de Erlang ha sido Kreditor. Es un claro ejemplo de desarrollo de aplicaciones web íntegramente desarrolladas con Erlang a nivel de servidor. cada vez más empresas de software están prestando servicios online en lugar de vender productos. Esta empresa se dedica al pago por Internet y pasó en 7 años a tener 600 empleados.2. Desde que surgió el modelo Cloud. la aplicación actualmente más relevante para el intercambio y envío de mensajes entre smartphones emplea a nivel de servidor sistemas desarrollados en Erlang. Estos escenarios junto con servicios bastante pesados e infraestructuras no muy potentes hacen cada vez más necesarias herramientas como Erlang.com/ https://mikoagenda. Software libre Hay muchas muestras de proyectos de gran envergadura de muy diversa índole creados en base a Erlang. por lo que en estos momentos será más extensa que la lista presente en estas páginas. que cambió 6 su nombre a Klarna AB .com/p/donde-se-usa-erlang. En el terreno del desarrollo web comienzan a abrirse paso también 7 empresas españolas como Mikoagenda . 8 4. e incluso a ataques de denegación de servicio. El siguiente listado se muestra como ejemplo: • Base de Datos Distribuidas 6 7 8 https://klarna.com/es http://aprendiendo-erlang.blogspot. Nota Aprovechando que se ha comenzado a hacer esta lista de software libre desarrollado en Erlang se ha estructurado y ampliado la página correspondiente a Erlang en Wikipedia (en inglés de momento y poco a poco en castellano). En la web de Aprendiendo Erlang mantienen un listado mixto de software libre y empresas que emplean Erlang. La mayoría de ellos se centra en entornos en los que se saca gran ventaja de la gestión de concurrencia y distribución que realiza el sistema de Erlang.Lo que debes saber sobre Erlang WhatsApp. por lo que se enfrentan a un uso masificado por parte de sus usuarios.html 8 . Con replicación. es una base de datos documental con acceso a datos mediante HTTP y empleando el formato REST. monitorización. un sistema NoSQL que permite el cambio en caliente del esquema de datos de forma fácil que realiza auto-indexación y permite la distribución de los datos.erlang-web.com/Riak. Es uno de los proyectos que están acogidos en la fundación Apache. • Erlang Web .couchbase.amazon. El propio Yariv lo empleó para hacer un clon de twitter y se empleó inicialmente para la interfaz de chat para facebook. no ha tenido modificaciones por parte de Yariv desde hace unos años por lo que su uso ha decaído. Tiene unos scripts que se ejecutan a nivel de servidor bastante potentes y permite el uso de CGI y FastCGI.com/es/simpledb/ 12 http://www. es un sistema desarrollado por Erlang Solutions que trata igualmente las vistas y la parte del controlador pero tampoco la parte de la base de datos. • BeepBeep .org/ 10 11 9 . Fue desarrollada por Amazon. tal y como indica su propia web (en castellano) es un almacén de datos no relacionales de alta disponibilidad flexible que descarga el trabajo de administración de las bases de datos. • SimpleDB .Lo que debes saber sobre Erlang 9 • Apache CouchDB . sólo existe (al menos es el más conocido en la comunidad) Yaws.org http://wiki. • Couchbase . Es usada por empresas como Mozilla y Comcast. es un framework inspirado en Rails y Merb aunque sin integración con base de datos.apache. • Frameworks Web • ErlyWeb . 16 15 14 13 12 11 10 9 http://couchdb. Se basa en una distribución de fácil escalado y completamente tolerante a fallos.html http://aws.org/ 14 https://github. • Servidores Web • Yaws .com/ 13 http://yaws. es una base de datos NoSQL para sistemas de misión crítica. tolerante a fallos y compatible con Memcached. Su configuración se realiza de forma bastante similar a Apache.com/davebryson/beepbeep/ 16 http://www. • Riak . con posibilidad de instalarse y configurarse para ello.basho. Es decir. una base de datos NoSQL inspirada en Dynamo (la base de datos NoSQL de Amazon).hyber.com/yariv/erlyweb 15 https://github. Como servidor web completo. definición de rutas. • CMS (Content Management System) • Zotonic .com/ 21 Content Management System. sistema CMS que permite el diseño de páginas web de forma sencilla a través de la programación de las vistas (DTL) y la gestión del contenido multimedia. es mostrar las comparaciones que empresas como Demonware o gente como el propio Joe Armstrong han realizado. texto y otros aspectos a través del interfaz de administración. es un framework pensado para facilitar la construcción de interfaces web. Nos permite agregar código HTML de una forma simple y enlazarlo con funcionalidad de JavaScript sin necesidad de escribir ni una sola línea de código JavaScript. Fue adquirido por SpringSource. una filial de VMWare en abril de 2010. KDE Talk. AJAX o similar en la que se haga necesario un comportamiento asíncrono sobre las conexiones síncronas. 23 22 20 21 18 5. quizás el más activo y completo de los frameworks web para Erlang a día de hoy. servidor de XMPP muy utilizado en el mundo Jabber.com/ http://www. Es usado en sitios como la BBC Radio LiveText.ejabberd. Erlang y la Concurrencia Una de las mejores pruebas de que Erlang/OTP funciona.chicagoboss.Lo que debes saber sobre Erlang 17 • Nitrogen . Sistema de Administración de Contenido 22 http://www. 20 http://zotonic.rabbitmq. Sistemas sometidos a un banco de pruebas 17 18 19 http://nitrogenproject. Chat de Tuenti. plantillas (ErlyDTL). Tiene implementación de vistas.im/ 23 http://www. LiveJournal Talk. Chat de Facebook. controladores y modelos a 19 través de un sistema ORM .org/ Object Relational Mapping. • Chat • ejabberd . sistema empleado para realizar la transformación entre objetos y tablas para emplear directamente los objetos en código y que la información que estos manejen se almacene en una tabla de la base de datos. servidor de cola de mensajes muy utilizado en sistemas de entornos web con necesidad de este tipo de sistemas para conexiones de tipo websocket. • ChicagoBoss .com/ 10 . • Colas de Mensajes • RabbitMQ . Ovi de Nokia. etc. Este servidor permite el escalado y la gestión de multi-dominios. En 2005 construyeron su infraestructura en C++ y MySQL. que supuso un crecimiento constante de usuarios durante 5 meses continuados.1. Se seguía manteniendo a nivel interno C++ con lo que el código se había hecho difícil de mantener. el código se colgaba con frecuencia. Malcolm Dowse. Demonware es la empresa que trabaja con Activision y Blizzard dando soporte de los servidores de juegos multi-jugador XBox y PlayStation. Comenzaré por comentar el caso de la empresa Demonware. hasta llegar a Erlang.Lo que debes saber sobre Erlang para comprobar cómo rinden en producción real o cómo podrían rendir en entornos de pruebas controlados. Decenas de millones de fans de Call of Duty Black Ops testearon la carga de Erlang. De 500 a 50 mil peticiones por segundo. de la empresa Demoware (de Dublín). El caso de Demonware En la conferencia de Erlang Factory de Londrés. 5. pero esta vez lo detallaré con datos que aportó la propia compañía a través de Malcolm Dowse en la Erlang Factory de Londrés de 2011. En 2006 se reescribió toda la lógica de negocio en Python. Se pasó de 20 mil a 2. Fueron unos 4 meses de desarrollo con el que consiguieron que el sistema ya no se colgase. A finales de 2007 llegó Call of Duty 4. Además. La empresa tuvo que ampliar su nodo de 50 a 1850 servidores en varios 11 .5 millones de usuarios. Finalmente. en 2011. lo que suponía un grave problema. También se dotó de mejores herramientas de log y administración y se hacía más fácil desarrollar nuevas características en muchas menos líneas de código. en 2007. afortunadamente no se vieron en la situación de superar esa cifra. Después veremos el banco de pruebas que realizó Joe Armstrong sobre un servicio empleando un par de configuraciones de Apache y Yaws. se reescribió el código de los servidores de C++ con Erlang. La empresa se constituyó en 2003 y desde esa época hasta 2007 se mantuvieron modificando su tecnología para optimizar sus servidores. dictó una ponencia titulada Erlang and First-Person Shooters (Erlang y los Juegos en Primera Persona). que se mejorase y facilitase la configuración del sistema (en la versión C++ era necesario reiniciar para reconfigurar. Para entonces habían llegado a los 20 mil usuarios concurrentes. de la que ya comenté algo en la sección de uso de Erlang en el Sector empresarial. lo que implicaba desconectar a todos los jugadores). Su concurrencia de usuarios no superaba los 80 jugadores. Es bien conocido que este hecho pasa con todos los sistemas. por su programación. al recibir un número de peticiones excesivo. La forma en la que implementa la programación concurrente y la gran capacidad de escalabilidad. de otro. sin el cambio a Erlang la crisis podría haber sido un desastre.Lo que debes saber sobre Erlang centros de datos. un servidor. fuesen degradando su servicio hasta dejar de darlo. No obstante.html 12 . 5. Gracias a estos factores. han podido estar a la altura de prestar el servicio de los juegos en línea más usados y jugados de los últimos tiempos. teníamos que crecer. En palabras de Malcolm: fue una crisis para la compañía.sics.se/~joe/apachevsyaws. Demonware es una de las empresas que ha visto las ventajas de Erlang. Yaws contra Apache Es bastante conocido ya el famoso gráfico sobre la comparativa que realizaron Joe Armstrong y Ali Ghodsi entre Apache y Yaws. de un lado. un cliente para medición y 14 clientes para generar carga. ya que los recursos de un servidor son finitos. que hiciera que los servidores web. pueden pasar cosas como las que se visualizan en el gráfico: 24 En gris oscuro (marcando el punto con un círculo y ocupando las líneas superiores del gráfico) puede verse la respuesta de Yaws en escala de 24 http://www. La prueba es bastante fácil.2. La prueba propuesta era generar un ataque de denegación de servicio (DoS). Cada nodo de Erlang puede manejar en total unos 2 millones de procesos. suele ser más lenta que la que proporciona el sistema operativo. En este caso. pasa algo parecido a lo visto con Demonware en la sección anterior. Yaws se mantiene con el mismo rendimiento hasta llegar a superar las 80 mil peticiones simultáneas. 13 . en parte debido a su integración íntimamente ligada al sistema operativo. Apache no puede procesar más de 4000 peticiones simultáneas. Las líneas que se cortan a partir de las 4 mil peticiones corresponden a dos configuraciones diferentes de Apache (en negro y gris claro). pero sin duda la escalabilidad y el rendimiento que se consigue pueden paliar ese hecho.Lo que debes saber sobre Erlang KB/s (eje Y) frente a carga (eje X). En sí. que le limita. Sin embargo. Erlang está construido con gestión de procesos propia y desligada del sistema operativo. Hay que entender que es un lenguaje basado en Prolog y con tintes de Lisp por lo que se asemeja más a los lenguajes funcionales que a los imperativos. Por último está la operación interna que retorna el resultado que se quiere obtener. El lenguaje Sólo hay dos tipos de lenguajes: aquellos de los que la gente se queja y aquellos que nadie usa. Al tratarse de funciones matemáticas o proposiciones lógicas no existe una correlación entre imperativo y funcional. se podría hacer a través de una lista de comprensión: 14 . En este ejemplo puede verse la definición de la función area. que tienen una sintaxis muy parecida entre ellos. iterativos y declaración de funciones y clases también semejantes. con o sin procesamiento. el conjunto de todos esos valores. Para un código imperativo común como el que sigue: para i <. Cada elemento dentro de la función tiene un propósito: obtener un valor.1 hasta 10 hacer si clavar(i) = 'si' entonces martillea_clavo(i) fsi fpara No existe en Erlang un equivalente que pueda transcribir una acción imperativa como tal. como si se tratase de una proposición lógica. conforma el resultado.Capítulo 2. que tienen una sintaxis. Si en un lenguaje funcional lo que se quiere es clavar los clavos que seleccione la función clavar martilleando. Hay gente a la que termina gustándole y otras personas que lo consideran incómodo. Java y Perl o PHP. En Erlang y demás lenguajes funcionales. Lo mismo pasa con la rama de C/C++. La mayoría de personas comienzan programando en lenguajes como Basic. Modula-2 o Pascal. la sintaxis está diseñada como si se tratara de la definición de una función matemática o una proposición lógica. el uso de los bloques condicionales. A la declaración de parámetros le sigue el símbolo de consecución (->). —Bjarne Stroustrup Erlang tiene una sintaxis muy particular. Los parámetros requeridos para obtener su resultado son Base y Altura. Altura) -> Base * Altura. En los lenguajes imperativos la sintaxis se basa en la consecución de mandatos que el programador envía a través del código a la máquina. Para desarrollar en Erlang hay que pensar en el qué se quiere hacer más que en el cómo. Un ejemplo básico: area(Base. Tenemos que pensar en los datos que tenemos y qué datos queremos obtener como resultado. un tipo de dato bastante potente de Erlang y los registros. Emplearemos la denominación simples y complejos (o compuestos). Veremos lo necesario para poder escribir programas básicos de propósito general y entender esta breve introducción de una forma más detallada y clara. Esta función area es completamente equivalente a la anterior a nivel de ejecución: area( Base. pudiendo referirnos a cualquiera de las otras formas de categorización si la explicación resulta más clara. Hay que entender que para resolver problemas de forma funcional muchas veces la mentalidad imperativa es un obstáculo. También veremos las listas binarias. También se pueden emplear letras en mayúscula al inicio. Como datos de tipo complejo veremos las listas y tuplas. espacios y lo 15 . No obstante. números y/o subrayados. otras organizaciones podrían conducirnos a pensar en los datos como: escalares y conjuntos o atómicos y compuestos. identificarlos y usarlos correctamente. Tipos de Datos En Erlang se manejan varios tipos de datos.Clavos. Altura ) -> Base * Altura . la forma de organizarlos no es relevante con el fin de conocerlos. Es lo que nos conducirá a la solución. clavar(i) =:= 'si' ]. A lo largo de este capítulo revisaremos la base del lenguaje Erlang. Por hacer una distinción rápida podemos decir que se distinguen entre: simples y complejos. un tipo de dato derivado de las tuplas. Erlang es un lenguaje de formato libre. Se pueden insertar tantos espacios y saltos de línea entre símbolos como se quiera. 1.1. Como datos simples veremos en esta sección los átomos y los números. Átomos Los átomos son identificadores de tipo carácter que se emplean como palabras clave y ayudan a semantizar el código.El lenguaje [ martillea_clavo(X) || X <. Un átomo es una palabra que comienza por una letra en minúscula y va seguido de letras en mayúscula o minúscula. 1. true > is_atom(' eh??? '). pero también valores de representaciones de la tabla de caracteres.0). tal y como se ve en este ejemplo de código en la consola: > is_float(5). siempre y cuando encerremos la expresión entre comillas simples. false y undefined. Hay átomos que se emplean con mucha frecuencia como son: true. $1. Algunos ejemplos: > is_atom(cuadrado). 16 .El lenguaje que queramos. Al igual que en otros lenguajes. false > is_float(5. true Los átomos tienen como única finalidad ayudar al programador a identificar estructuras. true > is_atom(alerta_112). y se pueden asignar a una variable directamente. Los átomos junto con los números enteros y reales y las cadenas de texto componen lo que se conoce en otros lenguajes como literales. false > is_integer(5). true > is_atom(false). Números Enteros y Reales En Erlang. true > is_atom(alta_cliente). true > is_atom(bajaCliente). los números pueden ser de dos tipos. $!. true > is_integer(5. Erlang permite dar el valor de un carácter específico a través el uso de la sintaxis: $A. Esto retornará el valor numérico para el símbolo indicado tras el símbolo del dólar en la tabla de caracteres.2. algoritmos y código específico. true > is_atom('HOLA'). 1. Son los datos que tienen un significado de por sí. Nota Como literales se pueden especificar números. true > is_atom(a4).0). Una variable puede tener esta forma: > Pi = 3. Un ejemplo de variables conteniendo números: > Base = 2. Nota Los números se pueden indicar también anteponiendo la base en la que queremos expresarlos y usando como separador la almohadilla (#). números y subrayados como se necesiten o deseen. Análogamente 2#1011 representa un número binario y 16#f42a un número hexadecimal.3. 12947972063153419287126752624640 Esta característica hace de Erlang una plataforma muy precisa y adecuada para cálculos de intereses bancarios. son símbolos a los que se enlaza un valor y sólo uno a lo largo de toda la ejecución del algoritmo específico.1415. en caso de que contenga números. si queremos expresar los números en base octal. valores estadísticos.El lenguaje true Otra de las cosas que sorprende de Erlang es su precisión numérica. como en matemáticas. índices bursátiles. Si multiplicamos números muy altos veremos como el resultado sigue mostrándose en notación real. Por ejemplo. Esto quiere decir que cada variable durante su tiempo de vida sólo puede contener un valor. 3. lo haremos anteponiendo la base al número que queremos representar 8#124. operaciones de listas o emplearse como parámetro en llamadas a funciones. seguida de tantas letras. 2 17 . etc. true Sobre las variables se pueden efectuar expresiones aritméticas. El formato de las variables se inicia con una letra mayúscula. posición de puntos tridimensionales. Variables Las variables.1415 > Telefono = "666555444". "666555444" > Depuracion = true. 1. sin usar la notación científica que muestran otros lenguajes cuando una operación supera el límite de cálculo de los números enteros (o valores erróneos por overflow): > 102410241024 * 102410241024 * 1234567890. tarificación telefónica. Listas Las listas en Erlang son vectores de información heterogénea. mientras que si lo intentamos encajar con el valor 3 resulta en una excepción. Erlang maneja las listas como lenguaje de alto nivel. podemos emplear: > f(Base). permitiendo cosas como las listas de comprensión o la agregación y eliminación de elementos específicos como si de conjuntos se tratase. Las listas son una de las potencias de Erlang y otros lenguajes funcionales. 5. en modo declarativo.2. Al igual que en Lisp.El lenguaje > Altura = 5. ok > Base = 3. ya sean números.4. a nivel de consola y para no tener que salir y entrar cada vez que queramos que Erlang olvide el valor con el que se enlazó una variable. ** exception error: no match of right hand side value 3 Lo que está ocurriendo es que Base ya está enlazado al valor 2 y que la concordancia (o match) con el valor 2 es correcto. 10. Nota Para nuestras pruebas. tuplas u otras listas. Si una variable durante toda la ejecución de una función sólo puede contener un determinado valor 1 el comportamiento de dicha función es muy fácilmente verificable . 3 Para eliminar todas las variables que tenga memorizadas la consola se puede emplear: f(). queremos que Base tenga el valor 3 en lugar del valor 2 inicialmente asignado veríamos lo siguiente: > Base = 2. átomos.2 > Base * Altura. 1 Muestra de ello es dialyzer. 2 > Base = 3. La ventaja de la asignación única es la facilidad de analizar código aunque muchas veces no se considere así.4 Si en un momento dado. una buena herramienta para comprobar el código escrito en Erlang. pueden contener información de distintos tipos. 18 . 1. es decir. 2 > T2. [1. 19 .3] Otro de los usos comunes de las listas es la forma en la que se puede ir tomando elementos de la cabecera de la lista dejando el resto en otra sublista. > [1. Esto se realiza con esta sencilla sintaxis: > [H|T] = [1.0.4].4] > H.0. ¿Qué podemos hacer con una lista? Una lista de elementos se puede definir de forma directa tal y como se presenta a continuación: > [ 1. [2.2.2. [1.1.3.1] > Extrae.[2].4] De esta forma tan sencilla la implementación de los conocidos algoritmos de push y pop de inserción y extracción en pilas resultan tan triviales como: > Lista = []. hola ].hola] A estas listas se les pueden agregar o sustraer elementos con los operadores especiales ++ y --.5.4] > H1. [1.El lenguaje 1.H2|T2] = [1. [] > Lista2 = [1|Lista]. 4. [1.2. 5 ]. 1 > T.2."Hola". [2.3.2.1] > [Extrae|Lista2] = Lista3.3.4]. [1. 1 > H2. [2.4.4] > [H1.3] ++ [4].3. 3.3.5] > [ 1.3. [3.3. "Hola". 2 > Lista2.2.4. [1] > Lista3 = [2|Lista2]. [1. 5.3] -.2. 2.2.4]. Tal y como se presenta en los siguientes ejemplos: > [1. Una demostración: > "Hola" = [72. automáticamente cobra el valor necesario para que la ecuación sea cierta. la asignación no da ningún error ya que ambos valores.El lenguaje [1] No obstante.97]. Erlang intenta hacer siempre que los elementos a ambos lados del signo de asignación sean iguales. En Erlang.108. la representación de la palabra Hola en forma de lista.4. mundo!" Una de las ventajas de la asignación propia de que dispone Erlang es que si encuentra una variable que no ha sido enlazada a ningún valor. de modo que la concatenación se podría realizar de la siguiente forma: > "Hola. se puede hacer como lista de enteros que representan a cada una de las letras o como el texto encerrado entre comillas dobles ("). "Hola. a izquierda y derecha. Erlang detecta que si una lista en su totalidad cumple con esta premisa. la única diferencia es que cada dato no es de 8 bits sino que es un entero lo que conlleva un mayor consumo de memoria pero mejor soporte de nuevas tablas como la de UTF-16 o las extensiones del UTF-8 y similares. Cadenas de Texto Las cadenas de texto son un tipo específico de lista. Un ejemplo: 20 . 1. el no poder mantener una única variable para la pila dificulta su uso. Al igual que con el resto de listas. en donde el tipo de dato char es un dato de 8 bits en el que se puede almacenar un valor de 0 a 255 y que las funciones de impresión tomarán como representaciones de la tabla de caracteres en uso por el sistema. Por tanto. Importante Esta forma de tratar las cadenas es muy similar a la que se emplea en lenguaje C. " ++ "mundo!". Este asunto lo analizaremos más adelante con el tratamiento de los procesos y las funciones.2. son el mismo para Erlang. Se trata de una lista homogénea de elementos representables como caracteres. las cadenas de caracteres soportan también la agregación de elementos. "Hola" Como puede apreciarse. es una cadena de caracteres.111. <<"H">> > T.111. mundo!" > A. ya que de otra forma el código para realizar el encaje sería mucho más complejo. <<"Hola ">> > B = <<"mundo!">>.$a>>.$l. "mundo!" Esta notación tiene sus limitaciones. se consume el doble de memoria para una cadena de caracteres almacenada en una lista en Erlang que en cualquier otro lenguaje. "Hola. en concreto la variable no asignada debe estar al final de la expresión. En este caso debe realizarse de la siguiente forma: > A = <<"Hola ">>. es decir.4. <<"Hola">> > H. la forma en la que tomábamos la cabeza de la lista en una variable y el resto lo dejábamos en otra variable. Por ejemplo. 1. B/binary>>. No se pueden agregar elementos ni emplear el formato de anexión y supresión de elementos tal y como se había visto antes. <<"ola">> La concatenación en el caso de las listas binarias no se realiza como con las listas normales empleando el operador ++. mundo!".El lenguaje > "Hola. La sintaxis de este tipo de listas es como sigue: > <<"Hola">>. <<"Hola">> La lista binaria no tiene las mismas funcionalidades que las listas vistas anteriormente.3. Las listas binarias permiten almacenar cadenas de caracteres con tamaño de byte y permite realizar trabajos específicos con secuencias de bytes o incluso a nivel de bit. " ++ A = "Hola.T/binary>> = <<"Hola">>. Listas binarias Las cadenas de caracteres se forman por conjuntos de enteros. Pero se puede hacer de otra forma más potente. <<"mundo!">> > C = <<A/binary. <<"Hola">> > <<72. se puede simular de la siguiente forma: > <<H:1/binary. 21 . 4. por ello en el ejemplo anterior la variable T se queda con el resto de la lista binaria. Trabajando con Bits En la sección anterior vimos la sintaxis básica para simular el comportamiento de la cadena al tomar la cabeza de una pila. se asume que es tanto como el tipo soporte y/o hasta encajar el valor al que debe de igualarse (si es posible). 11 Esta sintaxis es un poco más elaborada que la de las listas. En el caso anterior para cada una de las variables empleadas: > byte_size(A). little o big respectivamente. si es en formato Intel o Motorola. El tamaño está ligado al tipo. en orden de especificación: Endian-SignoTipo-Unidad. que empleará el formato nativo de la máquina en la que se esté ejecutando el código. siendo opcionales Tamaño y Tipo. Los tipos también tienen una forma compleja de formarse. Además de estos dos. Esta sintaxis se basa en el siguiente formato: Var:Tamaño/Tipo. > <<1215261793:32/big>>. 6 > byte_size(C). el cuantizador (o tipo) que hemos elegido es binary. ya que una unidad de medida no es nada sin su cuantizador. En caso de que el tamaño no se indique.El lenguaje <<"Hola mundo!">> Para obtener el tamaño de la lista binaria empleamos la función byte_size/1. <<"Hola">> > <<1215261793:32/little>>. Estos elementos son. 22 . 5 > byte_size(B). es posible elegir native. 1. El valor por defecto se prefija big. es decir.4. En este caso. ya que se pueden indicar varios elementos para completar la definición de los mismos. Este tipo indica que la variable será de tipo lista binaria. pero se debe a que nos adentramos en la verdadera potencia que tienen las listas binarias: el manejo de bits. con lo que el tamaño será referente a cuántos elementos de la lista contendrá la variable. 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. 88 1. Se pueden crear listas de tuplas para conformar conjuntos de datos homogéneos de elementos individuales heterogéneos. es decir. En caso de enteros y coma flotante el valor por defecto es 1.El lenguaje <<"aloH">> > <<1215261793:32/native>>. Los tipos disponibles son: integer. Se emplean para agrupar datos con un propósito específico. signed o unsigned. y en caso de binario es 8. por ejemplo. Tuplas Las tuplas son tipos de datos organizativos en Erlang. a los valores 20. <<"Hola">> En este ejemplo se ve que la máquina de la prueba es de tipo big u ordenación Intel. Si quisiéramos almacenar tres datos de color rojo. los bits que ocupa el elemento son 16 bits. si la unidad es 8 y el tamaño es 2. por ejemplo. • Unidad: este es el valor de la unidad. verde y azul en 16 bits. float y binary. 60:6>>. Según el tipo el tamaño es relevante para indicar precisión o número de bits. 0 y 6. tendríamos que la partición de los bits se podría hacer de forma algo dificultosa. Las tuplas. por ejemplo.5. • Signo: se indica si el número indicado se almacenará en formato con signo o sin él. no pueden incrementar ni decrementar su tamaño salvo por la redefinición completa de su estructura. a diferencia de las listas. Por lo tanto: Tamaño x Unidad = Número de bits. componer la cadena de 16 bits (2 bytes) correspondiente. 0:5. <<" <">> Nota Para obtener el tamaño de la lista binaria en bits podemos emplear la función bit_size/1 que nos retornará el tamaño de la lista binaria: > bit_size(<<"Hola mundo!"). respectivamente. • Tipo: es el tipo con el que se almacena el dato en memoria. 5 y 6 bits respectivamente. tomando para cada uno de ellos 5. 23 . por el que multiplicará el tamaño. sería así: > <<20:5. Con este manejo de bits. queremos consultar si debemos de realizar o no la depuración del sistema. 0}} }. Queremos almacenar esta información para poder tratarla y sabemos que va a ser: ruta. Se gestiona mediante la librería proplists. es decir. 24 . Se usa en aquellos casos en que es más fácil que acceder al elemento por un identificador conocido que por un índice que podría ser desconocido. "/"}.txt". tamaño y fecha de creación. que se ha cargado desde algún fichero o mediante cualquier otro método. Ahora supongamos que de esta lista.1.5. {{2011. 1. si ejecutásemos: > {date().5. las funciones de los módulos de su librería estándar. y los elementos separados por comas conforman su contenido. {{2011. 7}]. {debug. {days.El lenguaje Por ejemplo. 120. Supongamos que tenemos la siguiente muestra de datos: > A = [{path. 20}. Las listas de propiedades son muy usadas para almacenar configuraciones o en general cualquier información variable que se requiera almacenar. Estos arrays almacenan información de forma que sea posible rescatarla mediante el texto o identificador específico que se usó para almacenarla. A). mostrar mensajes de log si la propiedad debug es igual a true: > proplists:get_value(debug. Las llaves indican el inicio y fin de la definición de la tupla. es más fácil tratar y trabajar con fechas. trabajan con este formato. time()}. Por ejemplo.17}} Este tipo de dato también se emplea para emular los arrays asociativos (o hash). nombre. Nota En el ejemplo se puede ver que la fecha y hora se ha introducido de una forma un tanto peculiar. Esta información se podría almacenar en forma de tupla de la siguiente forma: { "/home/yo". imagina que tenemos un directorio con unos cuantos ficheros. true}.{22. 11.12. y si se emplea. En Erlang. Listas de Propiedades Una lista de propiedades es una lista de tuplas clave. 0. {0. valor.6}. "texto. 31}. Meses). 31}. telefono}). {septiembre. Un ejemplo de posible uso como tabla hash sería: > Meses = [ {enero. {diciembre. 31}. Registros Los registros son un tipo específico de tupla que facilita el acceso a los datos individuales dentro de la misma mediante un nombre y una sintaxis de acceso mucho más cómoda para el programador. Además. 31}. {febrero. 30}. > proplists:get_value(enero. 30}. 30}. Meses). 31}. 1. {agosto. {julio. {marzo. 30 El empleo de las listas de propiedades de esta forma nos facilita el acceso a los datos que sabemos que existen dentro de una colección (o lista) y extraer únicamente los que queramos obtener. Como los registros se emplean a nivel de preprocesador. 30}. apellidos. Internamente para Erlang. los registros realmente no existen. podemos cargar los registros existentes en un fichero y emplearlos desde la propia consola para definir datos o para emplear los comandos propios de manejo de datos con registros. Esto quiere decir que los registros en sí son una simplificación a nivel de uso de las tuplas. 25 . 28}. 31} ]. {noviembre.El lenguaje true Como es muy posible que no se sepan las claves que existen en un determinado momento dentro de la lista existen las funciones is_defined. o get_keys para poder obtener una lista de claves de la lista. Nota El módulo de proplists contiene muchas más funciones útiles para tratar este tipo de colección de datos de forma fácil. 31}. 31 > proplists:get_value(junio. No es mala idea dar un repaso al mismo para ver el partido que podemos sacarle en nuestros programas. {mayo.6. {junio. en la consola sólo podemos definir registros empleando un comando específico de consola. {octubre. A nivel de preprocesador son intercambiados por tuplas. La definición de registros desde la consola se realiza de la siguiente forma: > rd(agenda. {nombre. {abril. hrl cuyo contenido es la definición del registro agenda el tratamiento de la tupla se modifica automáticamente y ya podemos emplear la notación para registros de los ejemplos subsiguientes: > A = {agenda.El lenguaje Para declarar un registro desde un archivo el formato es el siguiente: -record(agenda. podemos realizar cualquiera de las siguientes acciones: > A#agenda. apellidos. {agenda. "Manuel". cuando se trata de códigos de tipo cabecera. si no tenemos en cuenta el identificador. {nombre. telefono})."Manuel"."Rubio".hrl").apellidos = "Rubio".nombre. sin embargo.666666666} > rr("registros. estos ficheros mantienen una extensión a medio camino entre los de cabecera de C (que tienen la extensión . También se pueden seguir empleando las funciones y elementos típicos de la tupla ya que a todos los efectos sigue siéndolo. la considera automáticamente como un registro. [agenda] > A. En estos ficheros se introducirán normalmente definiciones y registros. Nota Para obtener la posición dentro de la tupla de un campo. Si cargamos después el archivo registros.nombre Esto nos retornará la posición relativa definida como nombre con respecto a la tupla que contiene el registro de tipo agenda. Para tratar los datos de un registro. basta con escribirlo de la siguiente forma: #agenda. Nota Los ficheros de código de Erlang normalmente tiene la extensión erl. telefono = 666666666} Erlang reconoce como primer dato de la tupla el nombre del registro y como cuenta con el mismo número de elementos. 26 . 666666666}. Veamos con una pequeña prueba que si creamos una tupla A Erlang la reconoce como tupla de cuatro elementos. Su extensión es: hrl.h) y los de código normales de Erlang. "Rubio". #agenda{nombre = "Manuel". 666666666 > A#agenda{telefono=911232323}. tuplas o listas. #agenda{nombre = "Juan Antonio". accederemos indicando que es un registro (dato#registro.telefono.apellidos = "Rubio". Nos retornará illegal record info. Como los registros son internamente tuplas cada campo puede contener a su vez cualquier otro tipo de dato. Esta función tiene dos parámetros. record_info(fields. tal y como se ve en el ejemplo anterior.apellidos="Rubio"}. telefono = undefined} Recordemos siempre que la asignación sigue siendo única. no sólo átomos. esta estructura nos propone un sistema organizativo interesante para poder acceder directamente al dato que necesitemos en un momento dado facilitando la labor del programador enormemente. o size. Dentro de las llaves estableceremos tantas igualdades clave=valor como necesitemos (separadas por comas). podemos emplear la función record_info. #agenda{nombre = "Manuel". sino también otros registros. Para acceder al contenido de un dato de un campo del registro. A). Con ello. telefono = 911232323} > #agenda{nombre="Juan Antonio". los registros son entidades que trabajan a nivel de lenguaje pero Erlang no los contempla en tiempo de ejecución. Para obtener en un momento dado información sobre los registros. Importante Como se ha dicho anteriormente. Esto quiere decir que el preprocesador trabaja para convertir cada instrucción concerniente a registros para que sean relativas a tuplas y por tanto la función record_info no se puede emplear con variables. para retornar el número de campos que tiene la tupla donde se almacena el registro (incluído el identificativo. A#agenda en el ejemplo) y después agregaremos un punto y el nombre del campo al que queremos acceder. Para modificar los datos de un registro existente en lugar del punto emplearemos las llaves. 27 . el primero es un átomo que puede contener fields si queremos que retorne una lista de átomos con el nombre de cada campo.El lenguaje "Manuel" > A#agenda. Algo como lo siguiente: > A = agenda.apellidos = "Rubio". en nuestros ejemplos agenda). cadenas de texto o números. Antes de la letra c se pueden agregar un par de números separados por un punto. si quieres mostrar una cadena de texto por pantalla. El primer número indica el tamaño del campo y la justificación a izquierda o derecha según el signo positivo o negativo del número. esta función es equivalente y muy parecida a printf. Erlang no usa la barra invertida. Tenemos: ~ Imprime el símbolo de la virgulilla. por lo que se imprime la cadena de texto y seguidamente el retorno de la función (el retorno de función se imprime siempre en consola). Hola mundo!ok Esto sale así porque el retorno de la función es ok. en los que será necesario realizar una salida concreta de un dato con información más completa. debemos de insertar un caracter especial. El segundo número indica las veces que se repetirá el caracter. sino que emplea la virgulilla (~). del que emplearemos de momento sólo la función format.El lenguaje 2. Nota Para los que hayan programado con lenguajes tipo C. Esta función nos permite imprimir por pantalla la información que queramos mostrar basado en un formato específico que se pasa como primer parámetro. toda la información que vemos es porque la consola nos la muestra. los caracteres se interpretan de forma especial. Para hacer un retorno de carro. Imprimiendo por pantalla Muchas veces se nos presentará la necesidad de mostrar datos por pantalla. No obstante. c Representa un carácter que será reemplazado por el valor correspondiente pasado en la lista como segundo parámetro. la función se basa en una cadena de texto con un formato específico (agregando parámetros) que serán sustituidos por los valores que se indiquen en los parámetros siguientes.. Por ejemplo. y tras este símbolo.. hay momentos. Por ejemplo: 28 . Java. Para ello tenemos el módulo io. podemos escribir lo siguiente: > io:format("Hola mundo!"). es decir. PHP. A diferencia de otros lenguajes donde se usan los caracteres especiales. De momento. como resultado de salida del código que vamos escribiendo. . [104.000067e+4 | 10123.bbbbb. Hola]).$c. Hola.3c]~n". Tras el punto la precisión. Args). > io:format("[~s. [a.1.320 ok s Imprime una cadena de caracteres.3c. > io:format("~11. Data).~w. el tamaño que se quiere representar y justificación (como se vió antes).5s]". [$a. El primero es el dato que se va a imprimir.2f.32]."hola". El formato de e es científico (X.10.97]. Se usa sobretodo para poder imprimir tuplas. A partir de ese número agrega puntos suspensivos.0e+1. Un ejemplo: > Data = [{hola. 1. [Hola.~7.~-7. ccc. > io:format("[~w. Si imprimimos una lista con muchos elementos.10. [10.El lenguaje > io:format("[~c. pero el significado del segundo número en este caso es la cantidad de caracteres de la lista que se mostrará. Los números de anteposición se emplean de la misma forma que en s. 10. Un ejemplo: 29 .67. 10123.mundo}.10]ok > Args = [10000.~w. pero imprime igualmente listas.Hola ]ok w/W Imprime cualquier dato con su sintaxis estandar.4g]".230 | 1220.~7.~5c.mundo] ok La versión de W es similar a la anterior aunque toma dos parámetros de la lista de parámetros. el segundo es la profundidad. La única salvedad.Ye+Z) mientras que f lo presenta en formato con coma fija. y en caso contrario presenta el formato como si fuese e.10. etc. [{hola.7g ".10.~-5.1. podemos mostrar únicamente un número determinado de ellos. Veamos algunos ejemplos: > Hola = "Hola mundo!".1]).ddd ] ok e/f/g Se encargan de presentar números en coma flotante.~-7s.111. Los números que se pueden anteponer a cada letra indican.108. [Hola mundo!.$b.23. El formato g es una mezcla ya que presenta el formato científico si el número se sale del rango [0. 10. 1220.10.3f | ~11. números. átomos. Unos ejemplos: > io:format("[~7. es que una cadena de caracteres será considerada como una lista.2e.~w]~n".7e | ~11.mundo].Hola mu.mundo}.$d]). Similar a c.1.10000.0].~5. [ 1. 5]. consecutivo al valor a representar. hace un salto de línea. La diferencia entre las mayúsculas y minúsculas es precisamente esa.2. también es igual a su homónimo W. la retorna como cadena de caracteres. [[1.3]).2|.21. es que en lugar de presentar por pantalla la cadena resultante. Los números anteriores a cada letra (o símbolo) indican.16#15]ok i Ignora el parámetro que toque emplear. n Retorno de carro. Es útil si el formato de los parámetros que se pasa es siempre el mismo y en un formato específico se desea ignorar uno concreto.~. El símobolo de almohadilla (#) siempre antepone la base en formato Erlang: 10#20 (decimal). Nota Existe también el módulo io_lib que dispone también de la función format. [10101.]]ok p/P Es igual que w. La versión en mayúscula. b/B/x/X/+/# Imprimen números según la base indicada. La única diferencia que presenta..."0x". Un ejemplo: > io:format("[~. 16#1A (hexadecimal). pero intenta detectar si una lista es una cadena de caracteres para imprimirla como tal.21]).0x15. la representación de las letras de las bases mayores a 10 en mayúsculas o minúsculas.~. Con X se puede emplear un prefijo que se toma del siguiente parámetro que haya en la lista de parámetros.3. La diferencia entre ellos es que B imprime sólo la representación numérica.16#]". 30 . de modo que se pueda separar por líneas diferentes lo que se desee imprimir por pantalla. Si la impresión es demasiado grande.4.2b. [21. el primero la magnitud y justificación de la representación y el segundo la base en la que se expresará el número. 8#65 (octal). aceptando un parámetro extra para profundidad. [[1.El lenguaje > io:format("[~W]".16x. la parte en varias líneas. 10. Seconds. Fechas y Horas El manejo de fechas y horas en Erlang no se realiza con un tipo estándar.5}} Para obtener la fecha y hora en la zona horaria local podemos emplear también estas otras funciones dentro de una tupla de dos elementos: {date(). en una tupla {MegaSeconds. {{2012.S.22}.5} Una fecha y hora completa se representa a través de otra tupla que contiene en su interior las tuplas mencionadas antes. 2 El formato de POSIX para fecha y hora consiste en un número entero que corresponde al número de segundos transcurrido desde el 1 de enero de 1970 hasta la fecha que se indique. pero hay más funciones de tratamiento de fecha que emplean este formato.El lenguaje 3.5. MicroSeconds}. indicar que las fechas también pueden ser convertidas o empleadas en formato UTC (o GMT). en ese orden. mes y día. Podemos convertir una fecha a formato UTC (erlang:localtime_to_universaltime/1) o viceversa (erlang:universaltime_to_localtime/1). El tiempo también se maneja en una tupla de tres elementos en la que se pueden diferenciar en este orden: hora. que retornan la fecha y hora actuales 2 en formato POSIX ._} = now().{22. 1337717405 Por último. M*1000000+S. separadas en dos elementos diferenciados. minutos y segundos. time()} Hay otras funciones como now/0. sino que se establece como un término encerrado en una tupla.5. es decir. La función interna date/0 retorna este formato.10. lo que quiere decir que el cálculo de la hora en un sólo entero sería así: > {M. 31 . un formato como el siguiente: > erlang:localtime().22} Es una tupla compuesta por tres campos enteros destinados al año. Un ejemplo sería el siguiente: {22. Una fecha tiene la siguiente forma de tupla: {2012. por lo que las fechas que se proporcionen para la conversión a segundos. 32 . Esto da la posibilidad de dar fechas anteriores a 1970. el día de la semana de una fecha concreta (iso_week_number/0 e iso_week_number/1). Este módulo. tiene la capacidad de trabajar con segundos gregorianos en lugar de POSIX. 3 La toma de segundos siempre es en formato UTC (o GMT). en lugar de 1970. serán tomadas como en hora local y convertidas a UTC antes de su conversión a segundos.El lenguaje Nota El módulo calendar provee una serie de funciones que permiten averiguar si el año introducido es bisiesto (is_leap_year/1). además. el último día del mes (last_day_of_the_month/2) y más aún. El número obtenido en segundos 3 (para representación interna) es contado desde el año cero . Expresiones Las expresiones son la conjunción de símbolos con datos para conformar una sentencia válida para el lenguaje con significado para el compilador.3333333333333335 > 10 div 3. como la suma. 6 > 10 / 3. 3. Las expresiones pueden ser de tipo aritmético o lógico. 1 33 . Expresiones. 4 > 2 . de forma nativa. —David Heinemeier Hansson En este capítulo ampliamos lo visto en el capítulo anterior con el conocimiento de las expresiones lógicas. en tiempo de ejecución. Otras operaciones como la división entera o el remanente (o módulo) se implementan en cada lenguaje de una forma distinta. Las más básicas. por lo que haremos un repaso rápido con un breve ejemplo: > 2 + 2. resta. se pueden llevar a cabo expresiones aritméticas. 3 > 10 rem 3. las estructuras de control y el manejo de las excepciones. Estructuras y Excepciones La mejor forma de predecir el futuro es implementarlo. de modo que pueda ofrecer. multiplicación y división son de sobra conocidas. De un conjunto de datos dados con las operaciones indicadas y el orden representado por la expresión se obtiene un resultado. 1. una representación a nivel de código máquina del resultado que se pretende obtener.2. 1.1. las expresiones aritméticas.Capítulo 3. En las lógicas se busca una conclusión lógica (o binaria) a la conjunción de los predicados expuestos. Expresiones Aritméticas Con los números. 0 > 2 * 3. Las aritméticas buscan un valor a través de operaciones matemáticas simples o complejas. true > C1 and (C2 or C3). true > C3 = 3 =:= (1 + 2).Expresiones. También podemos encontrarnos con que queremos almacenar el resultado. 8 > 3 * 3 * 3. Ejemplos de todo esto: > 2 * 3 + 1. (bnot 2#101) band 2#11. Un ejemplo: > 3 > 2 > 2 > 7 > 2 1 bxor 2. por ejemplo. Podríamos hacer: > C1 = 2 > 1. sino que obtenemos resultados binarios únicos como true o false. Expresiones Lógicas Vamos a ver los operadores que se emplean en el álgebra de Boole band (binary and). 3 band 6. Estos operadores tratan los números como binarios y operan con el valor de cada una de sus posiciones (ceros o unos). false > C1 and C2. por ejemplo multiplicando más de dos operandos. o emplear el valor lógico de una serie de comparaciones. bor (binary or) y bxor (binary exclusive or). true > C2 = 1 > 2. 27 1. 7 > 2 * (3 + 1). true Podemos construir todas las expresiones lógicas que queramos de modo que a nivel de comparación podamos obtener un resultado 34 . anteponer una suma a una multiplicación. 2#011 bor 2#100.2. Estas herramientas nos facilitan operar de forma binaria con los números. 1 bxor 3. Para ello ya no operamos de forma binaria. false > C1 or C2. También se pueden realizar operaciones encadenadas. Estructuras y Excepciones Se puede hacer uso de los paréntesis para establecer una relación de precedencia de operadores para. tuplas. Por ejemplo: is_list(List) andalso length(List) Si List no fuese una lista. realizan una comprobación vaga. en Erlang existen otros como andalso y orelse. retornando inmediatamente el valor false. Estructuras y Excepciones lógico (verdadero o falso).bor bxor bsl bsr or xor ++ -== /= =< < >= > =:= =/= Descripción Ejecución de funciones Resolución de registros Unitarios División. Lo único que varía es que los primeros realizan una comprobación absoluta de los valores pasados. Comparaciones 35 . evaluando y comparando todos los valores. Multiplicación e Y lógico. Al emplear andalso esto no sucede. Son útiles si la comprobación se debe hacer consultado una función que tiene un coste de comprobación asociado. Nota Además de los operadores and y or. ya que muchas veces es mejor ahorrarse esas ejecuciones. 1. resta y O inclusivo y exclusivo. Agrega/Sustrae de conjuntos/ listas. Precedencia de Operadores El orden de los operadores para Erlang de más prioritario a menos prioritario es el siguiente: Operador : # + . si es falsa. Suma. y al obtener false finaliza las comprobaciones. En la siguiente sección se mencionan todos los operadores de comparación que se pueden emplear para realizar comparaciones entre cadenas. mientras que los presentados recientemente.3. ya se sabe que el resultado general será falso. números. El resultado a nivel de cálculo es el mismo. la ejecución de length/1 fallaría. en caso de andalso (por ejemplo). listas y/o registros. por lo que no se comprueba la segunda parte. Lo mismo se aplica a una comprobación que pueda fallar por lo que necesitamos otra anterior que descarta la segunda. Esto quiere decir que se evalúa la primera parte de la expresión y.Expresiones. ya que sólo se comprueba la primera parte.bnot not / * div rem band and + . Estas estructuras se basan en la concordancia de sus expresiones. Me refiero a la concordancia (en inglés match).Expresiones. podemos hacer un simple concordancia haciendo: [1. Podríamos definir esta expresión como la cualidad de una estructura de datos de asemejarse a otra. Ambas tienen que realizar una concordancia positiva con una expresión y ejecutar un código que retorne un valor.3] Si realizamos esta asignación.2. y para la mayoría de estructuras en ejecución dentro de la programación de Erlang. Como el que encajen los valores es tan importante para estas estructuras.3]. se acepta que el valor de la izquierda es igual al de la derecha (como en matemáticas: es un aserto válido). difieren.2. si tenemos el dato de la derecha que lo desconocemos. podemos hacer: [A. es decir. Ahora bien.2. lo que facilitará mucho la programación en este lenguaje. veremos que nos da como resultado [1. en general. Si tenemos un conjunto de datos.C] = [1.3] = [1. como habíamos visto en la listas.2. Estructuras y Excepciones Operador andalso orelse =! catch Descripción Y lógico con comprobación vaga O lógico con comprobación vaga Asignación y Paso de mensaje Captura de errores 2. 2.3] 36 . Estructuras de Control A diferencia de los lenguajes imperativos en Erlang sólo hay dos estructuras de control: if y case. incluso aunque haya que aplicar asignación para ello. dedicaremos una parte a estudiar lo que llamaremos a partir de ahora como concordancia y seguidamente veremos las estructuras donde se aplica. Concordancia En este apartado revisaremos un aspecto bastante importante en lo que respecta a la programación en Erlang y que conviene tener interiorizado.1. aunque se puedan parecer a las estructuras que existen en otros lenguajes. por ejemplo una lista.B. número o registro). En la sección de listas comentamos más formas de hacer concordancia a través de la agregación de conjunto (++) o con la lista en formato cabezacola ([H|T]).Expresiones. la denominación de funcional.18.25 En este ejemplo podemos ver cómo. pero podemos ignorar los que no nos interesen de la siguiente forma: {A. Como dijimos en un principio. Estructura case La primera estructura de control que vamos a tratar. 0. Es aconsejable acabar con un subrayado (_) que casa con todo y tomarlo como valor por defecto.2. B y C de los valores 1.3]. Estructuras y Excepciones Esto nos dará como resultado la asociación a A. respectivamente.2. por lo que retornará como en el caso anterior. ya que la tupla tiene valores fijos. 2. _ -> 0 end. la estructura no podría retornar nada y daría un error._.C} = {1. si la estructura que se indica en case casa con cualquiera que se suceda en las subsiguientes líneas. es case. retornando el resultado de la ejecución de dicho bloque (en este ejemplo sólo un valor). probablemente la más usada. le decimos al sistema que en ese espacio debe de haber un dato (del tipo que sea: lista. Si no se encontrase ningún valor que casara. átomo. Con respecto a las tuplas. [1. iva -> 0. implica que cada acción.2. Las estructuras de control como case no son una excepción.3} Con el símbolo de subrayado (o guión bajo "_"). estructura y función debe retornar un valor. 2 y 3. Veamos un ejemplo: > Impuesto = case irpf of irpf -> 0. esto no es aplicable. a menos que se quiera expresamente que falle en caso de que no se contenga un valor apropiado. tupla. se ejecuta un bloque concreto. Podemos ver otro ejemplo más complejo como el siguiente: > Resultado = case Fecha of 37 . Esta estructura toma un valor inicial como referencia y busca entre las opciones que se especifican la primera que concuerde para ejecutar su bloque funcional y retornar el valor que establezca la elección.25. pero que no nos interesa. "/".Agno:4/binary>> when is_binary(Fecha) -> binary_to_list(Agno) ++ "-" ++ binary_to_list(Mes) ++ "-" ++ binary_to_list(Dia)."/".Mes:2/binary. tomará cada parte y lo representará análogamente. tal y como se ve en el siguiente ejemplo: > Resultado = case Fecha of {D.". en caso de que se quiera el comportamiento del y lógico. or o orelse. is_integer(M).".Expresiones. Con esto nos aseguramos de que los valores que se parsearán dentro de cada bloque son del tipo que se esperan. 38 .M. _ -> "" end. is_integer(A) -> integer_to_list(A) ++ "-" ++ integer_to_list(M) ++ "-" ++ integer_to_list(D). convertirá cada dato y lo concatenará con los guiones para retornarlo en modo texto con formato A-M-D." como and. Si la variable Fecha la igualamos al retorno de la función date() el sistema entenderá que casa con el primer bloque. Estas expresiones se pueden agregar empleando conexiones como: andalso o ". En caso de no casar con ninguno de los anteriores. <<Dia:2/binary. o andalso.A} -> integer_to_list(A) ++ "-" ++ integer_to_list(M) ++ "-" ++ integer_to_list(D). <<Dia:2/binary.Mes:2/binary. La estructura case puede agregar condicionales a cada opción para la 1 concordancia."/". Si lo que enviamos es un texto en una lista binaria separado por barras inclinadas (/). Esto es lo que se conoce como guardas .Agno:4/binary>> -> binary_to_list(Agno) ++ "-" ++ binary_to_list(Mes) ++ "-" ++ binary_to_list(Dia). Para las guardas se pueden emplear tanto ". La diferencia existente entre las tres formas es que el agregado also o else hace que sea una comprobación vaga pudiendo finalizar antes de 1 Esta expresión inglesa se ha traducido en sitios como aprendiendo erlang como guardas." y orelse o "."/". _ -> "" end. para conseguir el comportamiento del o inclusivo lógico. ya que es una tupla de 3 elementos. o ". retorna una cadena vacía.M. y que algo como una tupla que contenga listas de caracteres no haga fallar el primer bloque de opción. Estructuras y Excepciones {D. Estas guardas se agregan tras cada opción con la palabra clave when.A} when is_integer(D). > _ -> fail > end. Para aclarar mejor las diferencias veamos tres ejemplos de código similares pero que funcionan de forma bastante diferente: > case a of > _ when (a+1)=:=a or b=:=b -> ok. Estructura if Otra de las estructuras que se puede emplear con Erlang es if. La única diferencia radica en la eliminación de los bloques de concordancia.". Es decir mediante el uso de los signos de puntuación se ignorarán los fallos que puedan suceder en la evaluación. sólo emplea las guardas. Estructuras y Excepciones evaluar todos los predicados. 2." y ". Por ejemplo. Si nos fijamos bien esta estructura podría tomarse como una simplificación de la estructura case anterior. Esta estructura guarda cierta similitud con las que se emplean en los lenguajes imperativos. En caso de 39 . pasando a comprobar el siguiente bloque y devolviendo fail. Es decir. caso2. salvo porque debe existir una opción de código que sea ejecutable en caso de que la cláusula previa se cumpla. ok El uso de or nos da un error de código directamente. y si es sobre 11 y 20. La diferencia entre los signos ". la siguiente estructura if devuelve el caso1 si el día de hoy está entre los valores 1 y 10. Mediante el uso de orelse no nos da error. continuando con la evaluación de lo siguiente. sólo da como inválida la primera parte y pasa a comprobar el siguiente predicado. b=:=b -> ok. con el signo "." con andalso y orelse es que los signos capturan excepciones. considerando que la primera parte retorna false. Por último. pero ignora toda esa comprobación por ser errónea. ya que estamos sumando 1 a un átomo llamado a y eso da bad argument in arithmetic expression.Expresiones. además y en todo caso que se debe retornar siempre un valor. > _ -> fail > end. fail > case a of > _ when (a+1)=:=a . Los signos de puntuación se comportan de la misma forma en este caso. > _ -> fail > end. en lugar de tomar ese resultado como no válido e invalidar toda la comprobación como el caso anterior.3. * 1: syntax error before: '=:=' > case a of > _ when (a+1)=:=a orelse b=:=b -> ok. el valor de comodín no se hace sobre una variable que pueda contener cualquier valor (como en el caso de subrayado. mientras que.4. al igual que el resto de estructuras existentes en Erlang. debe de retornar un valor y en caso de no poder ejecutar ningún bloque de código para resolver la función o valor que debe devolver. es necesario encerrar la comparación entre paréntesis.D} = date(). {2012. > D >= 11 andalso D =< 20 -> caso2. En la misma tabla de precedencia de operadores se puede ver que and y or tienen más prioridad que las comparaciones. nos puede ayudar a extraer información sin problemas. se coloca de modo que apunte siempre hacia el símbolo de igualdad.) o la coma (. vamos a definir una acción por defecto: > Caso = if > D >= 1 andalso D =< 10 -> caso1. sino se emplea la palabra reservada true por tratarse de predicados lógicos. por ejemplo). El hecho de poder tener una estructura como las listas de comprensión. Estructuras y Excepciones ejecutarse la función mostrada con los valores mayores o iguales a 21 daría un error: > {A. 2.Expresiones. > (D >= 11) and (D =< 20) -> caso2 > end.M. > true -> unknown > end. 40 .25} > Caso = if > (D >= 1) and (D =< 10) -> caso1. A diferencia de la estructura case. Importante En otros lenguajes. ** exception error: no true branch found when evaluating an if expression Este error es debido a que esta estructura.4. como se vio en la tabla de precedencia de operadores. origina el fallo. Para que el sistema no nos falle cuando introduzcamos fechas a partir del día 21. en caso de que se usen éstos y no el punto y coma (. Listas de Comprensión Una de las ventajas de la programación funcional es sin duda su caracter declarativo. por lo que.) u orelse o andalso. el operador de mayor que (>) y menor que (<) se sitúa siempre antes del signo igual. según si es uno u otro. Expresiones.6]] 41 .4].[3. La tercera parte. Un truco bastante útil que yo empleo es compararlo con una sentencia SELECT de SQL. [[1. La primera es la proyección de los elementos.5]. En el caso del ejemplo se indicó que el valor de X debía de ser par (que su remanente fuese cero en una división por dos).[4. por lo que conviene que se tenga muy presente su forma. por ejemplo.6]]. pero siendo una matriz fija de 2xN. Las listas de comprensión tienen tres partes que se enmarcan dentro de los corchetes.[1. sería tan sencillo como: > [ X || X <.[5.1].4. la parte de la selección (la parte del FROM) y las condiciones de la selección para cada tupla (la parte del WHERE). Nota Las listas de comprensión son uno de los elementos más importantes del lenguaje.1].2.[4.2]. Estructuras y Excepciones indicando: de donde procede esta información.3.4] Si expresamos esto mismo en lenguaje natural sería algo así como: [ Dame X || Donde X es un elemento de la lista <. la utilidad que tienen con respecto a la selección y proyección de información y realizar pruebas hasta comprender su funcionamiento completa y correctamente. teniendo listas de listas. X rem 2 =:= 0 ]. cuál queremos que sea su formato de salida y las condiciones que debe de cumplir nos proporciona dicha información al instante.[3.2].5].[5. ya que tiene la parte de la proyección (inmediatamente después de SELECT).[6. si queremos sacar de una lista sólo los números pares.3].4]. para ser seleccionado. podemos realizar la siguiente selección: > A = [[1. Por ejemplo. es decir.4.3. separada por una coma de la anterior. Un ejemplo más completo.[2.5].[1.[6. tal que la condición X rem 2 =:= 0 se cumpla.2.[2. Las condiciones que debe de cumplir cada elemento de la lista. son las condiciones de la selección. indica la forma en la que se presentarán los datos o en la que queremos que se configure la salida de la ejecución de la lista de comprensión.3]. La segunda es la selección de los datos. [2. Esta parte está separada de la primera por dos pipes (||) y tiene una flecha de derecha a izquierda que indica a la derecha el origen de los datos y a la izquierda el patrón o forma de los datos.5]. 1. ** exception error: no match of right hand side value a > catch 1 = a. Si se genera un error.a}. Excepciones Erlang es tolerante a fallos.A. si el código maneja recursos que hay que tratar de llevar a una situación segura antes de que suceda lo inevitable. ya que ni siquiera comparten memoria (cada proceso tiene la suya propia y es otra de las propiedades de Erlang el nada compartido o share nothing en inglés). 3.[{erl_eval. catch permite transformarlo en un dato recibido por la función o instrucción que se hubiese ejecutado.3}]}} La ejecución de la primera expresión nos lleva a una excepción que propocaría la finalización de ejecución del proceso. concuerdan los números 4 y 6. X >= 4 ]. Estructuras y Excepciones > [ X || [Y.{{badmatch. Veamos un pequeño ejemplo desde la consola de Erlang: > 1 = a. en esta definición. X] <.6] La lista resultado nos muestra.X). Y rem 2 =:= 0. es preferible intentar de realizar algún tratamiento para esa excepción.Expresiones. 42 . ni la ejecución de uno está condicionada o afecta a otros procesos. Por lo que. 3. Pero también hay casos en los que. ¿qué suecede cuando un proceso encuentra un fallo o una situación inesperada por el programador? Normalmente se dispara una excepción que hace que el proceso muera. Erlang convierte esa excepción en un tipo de dato que se podría procesar a través de una estructura de control. el hecho de que el elemento Y deba de ser par y el elemento X mayor o igual a 4. porque. [4. Esto le viene dado por el empleo de procesos en lugar de hilos. Este comando se puede anteponer a la ejecución de una función o de cualquier instrucción. En el siguiente capítulo veremos que eso en muchos casos es asumible e incluso deseable. mientras que anteponiendo catch a la misma expresión. Si un proceso muere y deja su estado de memoria corrupto no afectará a otros procesos. Recoger excepciones: catch El primer tipo de instrucción que se introdujo en Erlang para la captura de errores y excepciones es catch. El tema de los procesos lo veremos en el siguiente capítulo de forma más extensa. dentro de una sublista de dos elementos a los que asociamos como (Y.expr. Ahora vamos a centrarnos en las excepciones. {'EXIT'. B=3. el sistema no produce un error. y cometemos un error: > A=2. gracias a catch. Además de esta técnica. ** exception throw: {fallo. que debe de ser manejado por el código que toma el retorno de esta instrucción. en unas pruebas podría aparecer el error y ser solucionado. el sistema se detiene en ese punto. Estos se lanzarían a través de throw.Expresiones. por si quisiéramos a otro nivel capturarlos para procesarlos.Error} -> casoError > end. > false -> caso2. sino que retorna el casoError. hace que consideremos el código como correcto. Importante En este caso es una mala idea haber capturado la excepción ya que tapa un error de código que hemos provocado y que. Lanzar una excepción Hay veces que. 3. > {'EXIT'. Podemos emplear asertos (afirmaciones que se toman como axioma) para que generen una excepción en ese punto. cuando no es así. Por ejemplo: > 2+3=5. podemos lanzar excepciones con mensajes de error concretos. En este caso."Esto ha fallado"} 43 . 5 Si empleamos variables para almacenar los valores. si el código es crítico y no debe de contener errores. Esto nos garantiza que. Estructuras y Excepciones Un ejemplo del uso de catch con case: > case catch 1 = a of > true -> caso1. Esto se puede hacer de muchas maneras. ** exception error: no match of right hand side value 4 Como el código es erróneo y 5 no es igual a 4. "Esto ha fallado"}). en lugar de capturar una excepción conviene provocarla. Podemos verlo más claro a través de un ejemplo: > throw({fallo. 5=A+A.2. que puede ser cualquiera de las tres: throw. Este bloque se presenta como los que existen en los lenguajes imperativos. > error:Razon -> Razon > end. Esta sentencia presenta también una zona en la que poder ejecutar acciones que se lleven a cabo tanto si el código falla como si no. todo esto se puede atrapar en el catch. A continuación y después de los dos puntos (:) está la variable que contendrá el mensaje en sí del error para poder emplearlo dentro del bloque de código de recuperación. La estructura try.1} En la parte de catch se declaran tres partes diferenciadas. y es un bloque de código que se agrega tras catch. 3. Por ejemplo. si queremos imprimir por pantalla un saludo falle o no el código: > try > a=1 > catch > error:Error -> Error > after > io:format("Adios~n") > end.catch try. el sistema trata este lanzamiento de excepción como un error real provocado por el usuario.Expresiones.. ya sea por fallo. throw o porque se haya ordenado al proceso acabar su ejecución. hasta que esa sección no termina (en este caso imprimir Adios por pantalla) la estructura no retorna el valor correspondiente a su ejecución (la excepción a través de la rama error:Error). {badmatch. Estas se detallan con su clase. más clara y potente que catch. Estructuras y Excepciones En caso de que quisiéramos capturarlo con catch.. > exit:Razon -> Razon. como after está dentro de la estructura.. exit o error.catch es una nueva forma de tratar los errores. Esta sección recibe el nombre de after. 44 . La parte try da cabida a ejecución de código que será observado por la estructura y en caso del lanzamiento de cualquier excepción. Adios {badmatch. Un ejemplo de esta estructura: > try > a = 1 > catch > throw:Term -> Term. por lo que se podría capturar como cualquier otro error provocado por el sistema..1} El código se ejecuta de modo que.3. ** exception error: no function clause matching.4. concordancia o por guardas. function_clause Cuando se llama a una función con parámetros incorrectos.. se prolongase. Estructuras y Excepciones Nota Podríamos profundizar más en estas estructuras. badmatch Suelen suceder cuando falla la concordancia (matching). ** exception error: no case clause matching hola if_clause Al igual que el resto de *_clause. Errores de ejecución más comunes En esta sección daremos un repaso a los errores de ejecución más comunes que suelen surgir en Erlang cuando programamos de forma que el lector pueda corregirlos rápidamente. en caso de que tuviese). ya que mantenerse en ejecución tras un fallo podría provocar una situación imprevista que. ya que es una práctica habitual el disponer de la misma.Expresiones. > case hola of adios -> "" end. se dispara esta excepción: > io:format("hola". además. Este se dispara cuando no hay concordancia con ningún bloque (y sus guardas. 45 .. 3... pero lo dejo en este punto porque me gusta más la filosofía de Erlang: let it crash (deja que falle). ** exception error: no true branch found when eval. El sistema indicará que no hay rama true disponible. ya sea al intentar asignar una estructura de datos sobre otra que no tiene la misma forma o cuando se intenta hacer una asignación sobre una variable que ya tiene un valor. > if false -> "" end. este error se dispara cuando no hay ninguna guarda del if aplicable. []. case_clause Prácticamente igual la anterior. ya sea en número. con lo que dificultaría aún más la detección del fallo. dentro de la cláusula case. []). que indica que el sistema debe de poder fallar para volver a iniciar su ejecución de forma normal. Expresiones. badfun Sucede cuando se intenta emplear una variable que no contiene una función. Sucede cuando se intenta realizar una operación con valores incorrectos (como una suma de un número con una lista) o divisiones por cero. porque son más o menos de los que soporta. en caso de no ser correctos. en este caso el error es debido a que a la función que contiene la variable. debemos de crear un bloque en nuestras funciones de validación de argumentos que. ** exception error: bad function hola badarity Es un caso específico de badfun. por lo que para emplearla. A=2. la lancen. ** exception error: bad argument undef Lanzada cuando se llama a una función que no está definida (no existe). Un ejemplo: > A = hola.. Estructuras y Excepciones > A=1._) -> ok end. A(uno). Un ejemplo: > A = fun(_. ** exception error: no match of right hand side value 2 badarg Se suele disparar cuando llamamos a una función con argumentos erróneos. 46 . ** exception error: undefined function lists:no_existe/0 badarith Esta excepción es para errores matemáticos (aritméticos).. Un ejemplo: > 27 / 0. A(12). ** exception error: bad argument in an arithmetic expr. se le pasa un número de argumentos que no puede manejar. Un ejemplo de función que dispone de esto: > io:format({hola}). A diferencia de las ya vistas esta excepción es introducida como una validación de argumentos por el programador fuera de las guardas. ya sea por su número de parámetros o por su nombre dentro del módulo: > lists:no_existe(). org/doc/efficiency_guide/advanced.. Esto puede pasar cuando: tenemos demasiados procesos limitados por el parámetro de procesos máximos (se puede ampliar).erlang. Para una mejor optimización del sistema y entendimiento del mismo podemos leer la Guía de 2 Eficiencia de Erlang (en inglés) . demasiados nodos conectados.. cause_clause.. lo cual facilita la detección de errores. 2 http://www. system_limit Se alcanzó el límite del sistema. Son lo suficientemente graves como para parar todo el sistema (la máquina virtual de Erlang al completo).) y Reason tendrá una descripción de las funciones que fueron llamadas. Nota A partir de la versión de Erlang R15. Estructuras y Excepciones ** exception error: interpreted function with arity 2 . o demasiados argumentos en una función.Expresiones. . se presentarán de la forma: {Error. Si capturamos estos errores. Reason} Donde Error puede tomar cualquiera de los valores indicados anteriormente (bararg. Importante Hay que tener especial cuidado con los errores de system_limit. en Reason se puede ver además el nombre del fichero y número de línea en el se realizó la llamada. function_clause..html 47 . etc. átomos demasiado grandes o demasiados átomos. para llegar a ese punto. el polimorfismo y otros aspectos más avanzados de funciones y módulos que permite Erlang. Organización del código El código en Erlang se organiza en módulos y dentro de cada módulo puedes encontrar funciones. El módulo del código anterior llamado mi_modulo debe guardarse en un fichero con el nombre mi_modulo. -export([mi_funcion/0]). 1. sino que la ejecución se realiza mediante una llamada a la función. —Refrán popular Hasta el momento hemos estado ejecutando el código desde la consola. mi_funcion() -> "Hola mundo!".erl. función. El módulo exporta.Capítulo 4. por ejemplo. Las funciones se podrían tratar como otras estructuras de control (como case o if). En esta sección revisaremos los conceptos de módulo. El código podría ser como sigue: -module(mi_modulo). ya que disponen de elementos similares aunque son elementos de definición. No se ejecutan en el momento como las estructuras de control. Anteriormente ya hemos visto algunos de estos módulos. Todas las pruebas y códigos de ejemplo vistos se han escrito pensando en que serán ejecutados desde la consola de la máquina virtual de Erlang. Un módulo se define en un fichero a través de unas instrucciones de preprocesador iniciales que nos permiten definir el nombre y las funciones que queremos exportar (para emplear desde fuera del módulo). Las funciones y módulos Divide y vencerás. en el que empleábamos el uso de funciones como get_value. sino que se realiza a través de la escritura de módulos en los que hay funciones. como el caso de proplists. o pone a 48 . Normalmente la programación en Erlang no se produce en la consola. Nota Una vez tengamos el fichero creado. "Hola mundo!" La máquina virtual de Erlang busca el fichero beam en su ruta de módulos por defecto y luego en el directorio actual. en nuestro ejemplo. lo carga y busca la función dentro del mismo. compilarlo es tan sencillo como ir a una consola del sistema operativo y ejecutar: erlc mi_modulo. En caso de que no encontrase la función retornaría un fallo. También es posible compilar un módulo en la consola de Erlang. en lugar de la anterior: -module(mi_modulo). 49 . dejándolo disponible para su uso. -compile([export_all]).Las funciones y módulos disposición de otros módulos y de la consola la posibilidad de usar la función mi_funcion. podemos obviar el hecho de que habrá funciones privadas para el módulo y dejarlas todas abiertas.erl Esto genera un fichero mi_modulo. Esta directiva le dice al compilador que exporte todas las funciones de modo que no haya que nombrarlas una a una en la sentencia export. Desde la consola de la máquina virtual podemos ejecutar: > mi_modulo:mi_funcion(). Esto se haría escribiendo esta cabecera. Para simplificar el tema de la exportación en la codificación de nuestros primeros módulos hasta que nos acostumbremos a ella.beam que será el que empleará la máquina virtual para acceder a las funciones creadas. cuya aridad (o número de parámetros) es cero. escribiendo: > c(mi_modulo) Lo cual compilará el código creando el fichero mencionado anteriormente. Si lo encuentra. Erlang establece el nombre de sus módulos de forma plana. get(Key) -> get_value(Key. al igual que la anterior.Las funciones y módulos Importante A diferencia de otros lenguajes donde los paquetes. La declaración export es una lista que puede contener tantas referencias de funciones como se deseen publicar. Ámbito de las funciones Cuando creamos un módulo podemos importar y exportar funciones dentro o hacia fuera de él. ya que se intentarían emplear las funciones del propio sistema Erlang. tenemos el código de este módulo: -module(traductor). Desde el punto de vista de la importación. e incluso pueden existir varias declaraciones diferentes de export dentro de un mismo módulo. Por ejemplo. Por ejemplo. -import(proplists. que se importan desde el módulo que se detalla como primer parámetro. tenemos disponible la función get_value del 50 . El módulo encapsula un conjunto de funciones que pueden ser accesibles por otros módulos si se especifica su exportación. Puede haber tantas declaraciones como se necesiten dentro de un módulo y cada declaración es sólo para la importación desde un módulo. data()). -export([get/1]). En este caso y desde el punto de vista de la exportación. se emplearía el que tuviese la fecha de compilación más reciente. y no estarían presentes en este nuevo módulo de fecha más reciente. "adios"}]. Esto quiere decir que si existe un módulo llamado mi_modulo e intentamos cargar otro módulo con el mismo nombre. esenciales para su funcionamiento. si se creara un módulo vacío de nombre erlang y se intentara cargar el sistema completo se detendría. [get_value/2]). La declaración import. {"bye". tanto a otros módulos que importasen traductor como a la consola. Hay que tener cuidado con el nombre de los módulos. 2. contiene una lista de funciones como segundo parámetro. data() -> [{"hi". "hola"}. módulos o librerías se pueden encontrar de modo jerárquico. estamos dando exclusivamente acceso a la función get con un parámetro. Las funciones y módulos módulo proplists de modo que no tengamos que llamarla de forma 1 fully qualified . Nota La importación es una técnica que puede hacer confuso el código escrito. Se recomienda no emplearla a menos que el uso masificado de la función en cuestión sea más beneficioso para la lectura del código que invocarla de manera fully qualified. 3. Polimorfismo y Concordancia Una de las particularidades de las funciones de Erlang, es que disponen de polimorfismo. Si tuviésemos que programar una función que tuviese algunos de sus parámetros con valores por defecto, podríamos emplear el polimorfismo tal y como se da en muchos otros lenguajes imperativos, definiendo dos funciones con distinto número de parámetros, de la siguiente 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, se ejecutaría la primera forma, ya que casa con el número de parámetros, y en cambio, si pasamos tres parámetros, se ejecutaría la segunda forma. En Erlang sin embargo este concepto se puede completar agregando la característica de la simple asignación y la concordancia, de modo que nos permite hacer algo como lo siguiente: area(cuadrado, Base) -> Base * Base; area(circulo, Radio) -> math:pi() * Radio * Radio. area(rectangulo, Base, Altura) -> Base * Altura; area(triangulo, Base, Altura) -> Base * Altura / 2. Cada función anterior nos retorna un área, dependiendo del número de argumentos pero además del contenido del primer parámetro. Gracias a ello, podemos tener funciones con el mismo número de parámetros y diferente comportamiento. Como se puede observar, el 1 Fully Qualified, deriviado de su uso en los nombres DNS como FQDN, reseña la llamada a una función empleando toda la ruta completa para poder localizarlo, es decir, empleando también el módulo. 51 Las funciones y módulos primer parámetro puede contener los valores: cuadrado, rectangulo, triangulo o circulo (sin acentuar, ya que son átomos). En caso de recibir, 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 de un mismo nombre de función para igual número de parámetros pero diferente contenido, se debe de separar la definición de una función de la siguiente a través del punto y coma (;), mientras que la última definición debe de llevar el punto final. Esto es así para que los bloques de funciones polimórficas de este tipo estén siempre agrupados, conformando una única estructura más legible. 4. Guardas Anteriormente ya vimos las guardas en las estructuras de control case e if. Como la estructura de función es tan similar a las estructuras de control, también contempla el uso de guardas, lo que le permite realizar un 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íamos reescribir 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 que las entradas de las variables sean numéricas o en caso contrario que no se ejecutaría esa función. Podríamos agregar en las condiciones que la Base sea mayor de 0, al igual que la Altura y Radio, y cualesquiera otras comprobaciones más que se nos puedieran ocurrir. 5. Clausuras Si revisamos un momento la teoría lo que ahora vamos a ver podría encajar perfectamente como clausura, lambda o función anónima. En principio, las definiciones: 52 Las funciones y módulos Se llama clausura (en inglés clousure) a una función junto a un entorno referenciado de variables no locales. Esto quiere decir que la función tiene 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 hay definida una variable llamada contador, esta variable será accesible también por calculadora. Por otro lado, tenemos el cálculo lambda, inventado por Alonzo Church y Stephen Kleen en 1930, que en un entorno matemático define lo que es una función para abstraer las ecuaciones en un lenguaje más simplificado (Peter Landin se encargó de llevar esta teoría a Algol 60). El caso es que la teoría de funciones, subprogramas y subrutinas se basa en 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 no se declaran con un nombre sino que son declaradas y almacenadas en una variable, de modo que la variable es empleada para hacer llamadas a otras funciones, pudiendo ser pasada como parámetro o retornada como resultado, ya que en sí, es tratada como un dato. Las clausuras de Erlang se basan en todas estas premisas. Son funciones que, al definirse, pueden tomar el valor de las variables del entorno en el que son definidas (ya que las variables son de simple asignación y toman su valor en ese momento), que cumplen con la adaptación del cálculo lambda de Church y Kleen y son anónimas puesto que su definición es como una instanciación que se almacena en una variable y puede ser 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 entorno en el que está siendo definida, en este caso la variable A. De este modo al ejecutar la función F, multiplica la variable que se le pasa como pará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ún los datos con los que haya sido llamada la primera: 53 Las funciones y módulos -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 dentro de otra, sino que la generación de las clausuras puede tener también guardas. Si quisiéramos agregar una clausura más al código, para truncar el 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 la misma forma, tanto si se envía un dato de tipo entero como si el dato es de 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 una clausura más empleando directamente F. Esto viene muy bien para cuando se tienen varias funciones para trabajar de una cierta forma y se desea pasar la función elegida como parámetro a un có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 54 Estas funciones realizan un tratamiento de datos como podría hacerlo un bucle en los lenguajes imperativos. Retomaremos este uso cuando tratemos el tema de la recursividad.0. filter o fold. se piensa en las listas de comprensión y en funciones sobre listas como son map.0. 55 . recibiendo cada elemento de la lista como parámetro y retornando un valor por cada llamada que será almacenado y retornado por map/2 al final de la ejecución de todos los elementos. podríamos emplear algo como lo siguiente: > E = infinitos:enteros(5). A través del uso de clausuras. podemos hacer que se aplique un código específico a cada elemento de una lista de elementos. En realidad termina siendo más potente ya que.0.3. enteros(Desde) -> fun() -> [Desde|enteros(Desde+1)] end. [6|#Fun<infinitos. [5|#Fun<infinitos. 6.16233373>] > [M|G] = F(). se puede paralelizar. o incluso con un contenido que no queremos que esté siempre presente.2. sino que se vaya generando a medida que se necesita. Por ejemplo: > L = [1. #Fun<infinitos. Veamos la lista de funciones más importantes de este tipo que provee Erlang: map/2 Se ejecuta la clausura pasada como parámetro. -compile([export_all]). debido a su naturaleza.16233373>] Aunque hemos creado una recursividad infinita (algo parecido a un bucle infinito). Programación Funcional Cuando se piensa en programación funcional. normalmente.Las funciones y módulos trabajar con una lista de infinitos términos. Desde consola. podemos realizar una clausura que haga algo como lo siguiente: -module(infinitos).4].16233373> > [N|F] = E(). gracias a la evaluación perezosa de Erlang cada número se va generando a medida que vamos avanzando. Si alguno de los elementos retorna true. [X]) end. > true -> false > end > end. salvo que foreach/2 no guarda el retorno de las clausuras que ejecuta ni lo retorna. > true -> false > end > end. L). Un ejemplo: > L = [1.4]. [2.4]. la función any/2 retorna también true. que forzosamente deben aceptar los dos parámetros. La última letra 56 . 1 2 3 4 ok foldl/3 . Es como si encadenase la ejecución de las clausuras.2.2. Un ejemplo: > L = [1.6.3.Las funciones y módulos > lists:map(fun(X) -> X * 2 end. true all/2 Igual que la anterior. En principio es igual que map/2. false foreach/2 Aplica la ejecución de la clausura a cada elemento de la lista. L).3. la función all/2 retornaría false.2.4].foldr/3 Esta función se encarga de ejecutar la clausura pasando como parámetro el elemento de la lista y el retorno de la ejecución anterior. > lists:all(fun(X) -> > if > X > 2 -> true.3. En el momento en el que uno retorne false. Por ejemplo: > L = [1. L). > lists:foreach(fun(X) -> io:format("~p~n".4. debiendo retornar ésta true o false. > lists:any(fun(X) -> > if > X > 2 -> true. con la salvedad de que todos los elementos evaluados deben retornar true.8] any/2 Se evalúa cada elemento con la clausura pasada como parámetro. L). 1. 57 . Left o izquierda sería desde la cabeza hasta la cola. > lists:mapfoldl(F. guardando el resultado de ejecución de cada clausura. el primero es la clausura. true -> false end end. Factorial) -> {X*2. La clausura debe retornar verdadero o falso (true o false).2. > F = fun(X) -> if X > 2 -> true.2. Factorial*X} end. A la función se le pasan tres parámetros. Un ejemplo: > L = [1.4]. L).2.4] takewhile/2 En este caso.24} filter/2 El filtrado toma la lista inicial y ejecuta la clausura para cada elemento. 24 mapfoldl/3 .4. [3. Un ejemplo: > L = [1.3. true -> false end end.2. > F = fun(X.mapfoldr/3 Estas funciones son una combinación de map/2 y fold/3. L). Por ejemplo: > L = [1. El retorno de la función clausura debe ser una tupla en la que el primer valor es el resultado de la parte map/2 y el segundo valor es el retorno para seguir encadenando. El retorno de ambas funciones es también una tupla en 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 la parte de fold/3.4].Las funciones y módulos (l o r) indica desde donde se inicia la toma de elementos de la lista.3. y right o derecha empezaría a tomar elementos por el final de la lista hasta el principio. > lists:takewhile(F. Encadenan los resultados de cada una de las clausuras de la anterior a la siguiente comenzando por un valor inicial. > lists:filter(F. 1.3. > lists:foldl(F. L).4]. la clausura se emplea como filtro al igual que con filter/2.4]. {[2.8]. el segundo el valor inicial y el tercero la lista a procesar: > L = [1.3. pero en el momento en el que un valor retorna falso termina la ejecución. Cada elemento que cumpla con la clausura será agregado a la lista del resultado de filter/2. > F = fun(X) -> if X =< 2 -> true.6. Factorial) -> Factorial * X end. > F = fun(X. L). 4]} Estas son las principales funciones que pertenecen al módulo lists.2]. [3. La mayoría de estas funciones ya han sido agregadas a lenguajes imperativos.3. No toma ningún elemento mientras se cumpla la condición.4]. al igual que las listas de comprensión. el resto se almacena en variables locales. puedes echar un vistazo al módulo lists y así ampliar tu vocabulario en Erlang. que toma todos los elementos que no tomaría takewhile/2. L). Es bueno conocer estas funciones para que cuando surja la necesidad de resolución de un problema se pueda recurrir a ellas si es posible. Es decir. que en la mayoría de lenguajes se almacena en una pila de ejecución. en el caso de la recursividad. son propias para cada ejecución aislada del problema.2. 7.3. > F = fun(X) -> if X =< 2 -> true. true -> false end end. Si estás interesado en saber más acerca de estas funciones. es que las variables locales que se emplean. El lazo común entre cada solución o ejecución de la función.[3. son los parámetros de entrada y los parámetros de salida. Un ejemplo: > L = [1. true -> false end end. 58 .2] dropwhile/2 Este es el complementario de takewhile. En el momento que se incumple la condición. > F = fun(X) -> if X =< 2 -> true. L).2. toma todos los elementos desde ese punto hasta el final. Un ejemplo: > L = [1.4] splitwidth/2 Divide la lista en dos sublistas de manera equivalente a introducir en una tupla como primer valor el resultado de takewhile/2 y como segundo valor el resultado de dropwhile/2. Recursividad La recursividad define el hecho de que una función se pueda llamar a sí misma para completar el procesamiento sobre una muestra de datos a la que se puede aplicar el mismo algoritmo de forma recurrente hasta conseguir una solución final. por lo que es posible que muchas de ellas sean ya conocidas para el lector. La diferencia entre la recursividad y realizar un código iterativo. > lists:dropwhile(F.Las funciones y módulos [1. > lists:splitwith(F. {[1.4]. L). {[5. para conseguir la solución global del problema. tenemos dos casos diferenciados. fact(0) -> 1. una recursividad sobre cada parte para descomponer el problema lo más que se pueda y una mezcla en la que se va realizando combinación de las partes ordenadas.1. convirtiendo el código recursivo en iterativo. Un tipo de algoritmos que se puede implementar muy fácilmente con recursión son los de divide y vencerás. recursión y combinación. Las tres partes que se pueden diferenciar en este algoritmo son: separación.2.3. Primero partimos la lista en trozos de tamaño similar.2. 7. Podemos ver algunos algoritmos clásicos como los de ordenación de listas que nos pueden ayudar a comprender mejor cómo funciona la recursividad. fact(X) -> X * fact(X-1). > {L1.8. que serían el resto de casos para una variable X lo que se resuelven multiplicando cada valor por su anterior hasta llegar a cero. idealmente igual: > L = [5.L2} = lists:split(length(L) div 2.8].[4. Ordenación por mezcla (mergesort) Comenzaremos viendo el algoritmo de ordenación por mezcla (o mergesort).2. que se basa en hacer una partición de los elementos simple. Estos algoritmos se basan en la división del problema en subproblemas más pequeños pero similares llegando a los casos particulares. porque sabemos que el factorial de cero es uno. El ejemplo más simple de recursividad es la operación de factorial: -module(fact). Esto evita que se produzcan errores por desbordamiento de pila. El caso particular representado por la primera declaración de función. En esta functión.4.3.2. que hace que la pila de una llamada a la siguiente se libere dado que el código para ejecutar en esa función ya no es necesario. Se resuelve cada pequeño problema de forma aislada y después se combinan las soluciones (si es necesario). Este algoritmo es simple en las dos primeras partes y deja la complejidad para la tercera. También disponemos del caso general. al menos a efectos de consumo de memoria.Las funciones y módulos Nota Erlang implementa un sistema denominado tail recursion (o recursividad de cola).1]} 59 . -compile(export_all).1]. tres con tres. que será la comparación de un elemento con otro elemento (uno con uno). que de forma recursiva se encarga de comparar los elementos de una lista con la otra para conformar una sola en la que estén todos ordenados. []) -> L. dadas dos listas ordenadas podríamos definirla así: mezcla([].L2)]. Como puedes observar. mezcla([H1|T1]=L1. 60 . ordena(L2)). La mezcla podemos hacerla a través de recursión también. por lo que hay que ir separando elementos hasta llegar al caso particular. Como dijimos al principio. el que cumpla con la condición indicada (el que sea menor). para cada sublista. El código completo del algoritmo es el siguiente: -module(mergesort). concatenando el elemento y llamando a la función con los elementos restantes. Para conseguir esto. la complejidad se presenta en la combinación. -export([ordena/1]). mezcla(ordena(L1).Las funciones y módulos Esto lo podemos dejar dentro de una función que se llame separa/1 para semantizar el código y diferenciarla dentro del algoritmo. y así hasta poder comparar la mitad de la lista con la otra mitad para acabar con la ordenación de la lista de números. de modo que. mezcla(L. o función mezcla/2.T2)] end. L) -> L. Después un nivel más alto de dos con dos. [H2|T2]=L2) -> if H1 =< H2 -> [H1|mezcla(T1. separa(L) -> lists:split(length(L) div 2. true -> [H2|mezcla(L1. Para que este algoritmo funcione ambas listas deben de estar ordenadas. L). antes de llamar a la mezcla. realizamos la siguiente recursión: ordena([]) -> []. con lo que llega hasta la comparación de un sólo elemento con otro. ordena([H]) -> [H]. La mezcla la realizamos tomando en cada paso de los datos de cabecera de las listas. se vuelve a llamar a la función ordena/1.L2} = separa(L). ordena(L) -> {L1. [Pivote|Mayor]}.4.T.7. X =< Pivote ]. ordena(L) -> {L1. es muy parecida a la de mergesort. una con los elementos menores al pivote (la primera) y la otra con los elementos mayores (la segunda). ordena(L2)). [H2|T2]=L2) -> if H1 =< H2 -> [H1|mezcla(T1.6.6. Viendo el código al completo: -module(quicksort).[5. vamos a llevarnos la complejidad de la parte de combinación a la parte de separación. > Menor = [ X || X <.3. que se llama quicksort por lo rápida que es ordenando elementos. 61 . > {Menor. La parte de la recursividad.6]} La parte de la mezcla es trivial puesto que se recibirán listas ya ordenadas como parámetros.L2)]. {[2.3.1]. mezcla(L. [1. ordena([H]) -> [H].Las funciones y módulos mezcla([]. Esta parte de código la simplificaremos empleando listas de comprensión. []) -> L.3.2. se basa en la ordenación primaria de las listas para que la mezcla sea trivial.2. de modo que podemos hacer lo siguiente: > [Pivote|T] = [5. Este algoritmo se basa en coger un elemento de la lista como pivote y separar la lista en dos sublistas. mezcla(ordena(L1).2]). mezcla([H1|T1]=L1. Ordenación rápida (quicksort) En este ejemplo. L) -> L.5. para volver a llamar al algoritmo para cada sublista. > Mayor = [ X || X <. de modo que para poder emplear el algoritmo habría que hacerlo así: > mergesort:ordena([1. La mezcla consiste sólo en concatenar las sublistas y retornar el resultado.5. X > Pivote ].L2} = separa(L).3.T2)] end.4. ordena([]) -> [].2.2. Hemos dejado exportada solamente la función ordena/1. true -> [H2|mezcla(L1.1]. Esta función.6.2.7] 7.T. X =< Pivote ]. e incluso las operaciones matemáticas.6. separa([]) -> {[]. integer_to_list/1. X > Pivote ].2. [Pivote]. retornando ambas sublistas y el pivote. L2} = separa(L). L2) -> L1 ++ L2. La ejecución de este código sería así: > quicksort:ordena([1.5.5. el redondeo de números (round/1) y el cálculo de la fecha (date/0) o la hora (time/0). lógicas y otras. Algunas de ellas ya las hemos visto: is_integer/1. 5 Estas funciones reciben el nombre de BIF (en inglés Built-In Functions). sino que el sistema las procesa a bajo nivel y forman parte de la máquina virtual como instrucciones base que se ejecutan mucho más rápido. Normalmente no hace falta referirse al módulo para emplearlas (a menos que exista ambigüedad). [Pivote].T. separa([Pivote|T]) -> Menor = [ X || X <. mezcla(L1. Otros ejemplos de BIFs son el cálculo de MD5 (md5/1). 62 . ordena([]) -> []. Por un lado separamos la lista en dos sublistas seleccionando un pivote. [1. {Menor. []}.3.T. Se puede ver que la estrategia de divide y vencerás se mantiene. ordena([H]) -> [H].2]).7. Estas funciones construidas en el sistema se albergan bajo el módulo erlang. Funciones Integradas En Erlang existen funciones que no están escritas en Erlang.6. []}. Un ejemplo: > erlang:'+'(2. separa([H]) -> {[H].7] 8. 3). []. Las sublistas se ordenan mediante recursión sobre cada sublista por separado. ordena(L2)). Mayor = [ X || X <. mezcla(ordena(L1) ++ [Pivote].Las funciones y módulos -export([ordena/1]).3. []. ordena(L) -> {L1. Mayor}. length/1. comentó en un artículo de su blog . uno de los creadores/fundadores/inventores de 2 Erlang.com. 2 http://rvirding. Un intento de definirlo por parte de Jonas Barklund y Robert Virding disponible en la especificación (no indica URL específica el autor en su blog). es que un BIF fue una parte del lenguaje Erlang que no disponía de una sintaxis concreta o especial. por lo que se mostraba como una llamada a función normal.es/2009/10/what-are-bifs.Las funciones y módulos Nota Robert Virding. lo confuso que resulta determinar qué es un BIF y qué no.html 63 .blogspot. 1. Repasaremos también las ventajas e inconvenientes que tienen los procesos de Erlang. por ejemplo.) y otras se pueden desarrollar sin estar a priori integradas dentro del proceso.. Su estructura aporta ventajas como la posibilidad de lanzar millones de procesos por nodo. o monitores. 64 . no sólo los que son propios de Erlang. Los procesos en Erlang son propios de la máquina virtual y en cada plataforma tienen las mismas características y se comportan de la misma forma. teniendo en cuenta que cada máquina puede ejecutar más de un nodo. Los procesos son unidades de un programa en ejecución que tienen un código propio y un espacio de datos propio (normalmente llamado heap). crecer (ampliando sus recursos asignados).. Para ello proporciona a cada proceso un buzón y la capacidad de enviar mensajes a otros procesos a través de la sintaxis del propio lenguaje. ya que puede nacer (crearse). También presenta inconvenientes como la velocidad de procesamiento frente a los procesos nativos del sistema operativo. Por último. no coches más rápidos.Capítulo 5. Linux. —Steve Swartz Una de las grandes fortalezas de la plataforma de Erlang es la gestión de procesos. . Se podría decir que un proceso cumple los principios del ser vivo. de una forma simple. Muchas de estas características están presentes en los procesos nativos de sistemas operativos como Unix o derivados (BSD. Procesos Cuando estás en un atasco de tráfico con un Porsche. no se emplean los mecanismos propios del sistema operativo para ello sino que es la propia máquina virtual quien provee los mecanismos para su gestión. En definitiva. los mecanismos de comunicación de que dispone y sus características de monitorización y enlazado con otros procesos. el sistema de compartición de información entre procesos programados para la concurrencia emplea el paso de mensajes en lugar de emplear mecanismos como la memoria compartida y semáforos. La escalabilidad va de construir carreteras más anchas. Solaris. todo lo que puedes hacer es consumir más combustible que el resto estando parado. de un hilo. Anatomía de un Proceso Un proceso cualquiera. Para comenzar analizaremos la anatomía de un proceso en Erlang para comprender para lo que es. tiene unas características específicas que lo distingue. mientras que el que está enlazado de forma informativa es notificado de su muerte. • Y son los primeros 15 bits del número del proceso. dentro del mismo nodo. para dar una visión a alto nivel del concepto. En Erlang el proceso es además un animal social. Esta sería la vida útil de un proceso. el proceso dispone de un buzón. ya sea dentro de la misma máquina o en la misma red. Como dijimos al principio. El planificador de procesos de la máquina virtual de Erlang se encarga de dar paso a cada proceso a su debido tiempo y de aprovechar los recursos propios de la máquina. Tiene mecanismos que le permiten comunicarse con el resto de procesos y enlazarse a otros procesos de forma vital o informativa. Para esta comunicación.Procesos reproducirse (generar otros procesos) y morir (terminar su ejecución). el proceso que está enlazado con él de forma vital muere también. Ventajas e inconvenientes Hemos realizado una introducción rápida y esquemática de lo que es un proceso en general y un proceso Erlang. en consola podemos ver su representación. • Z son los bits 16 a 18 del número del proceso. Actualmente Y y Z se siguen representando de forma separada para no romper esa compatibilidad. donde Y era de 15 bits y Z era un contador de reutilización. en forma de cadena. de modo que el proceso puede procesar estos mensajes en cualquier momento. un índice a la tabla de procesos. sus características especiales y sus ventajas e inconvenientes. siendo cero el nodo local. por tanto. como <X. sino que también es posible entre distintos nodos que estén interconectados entre sí. para intentar paralelizar y optimizar al máximo posible la ejecución de los procesos. En este buzón otros procesos pueden dejar mensajes encolados. En este 65 . viene de las versiones R9B y anteriores.Y. Los valores que se representan en esta forma equivalen a: • X es el número del nodo. los procesos en Erlang no son los del sistema operativo y. como son los procesadores disponibles. 2. El envío de estos mensajes no sólo se puede realizar de forma local. En caso de que un proceso muera (ya sea debido a un fallo o porque ya no haya más código que ejecutar).Z>. El hecho de que los valores Y y Z estén representados como dos valores aparte. Nota Cuando se lanza un proceso. tienen sus diferencias. siendo el valor de procesos máximo por defecto de 32. cualquier lenguaje que emplee directamente los procesos nativos del sistema operativo será más rápido. Desde el principio hemos remarcado siempre que una de las potencias de Erlang son sus procesos. Son procesos y no hilos. Existe un buzón en cada proceso al que se le puede enviar información (cualquier dato) y el código del proceso puede trabajar con ese dato de cualquier forma que necesite. En el caso de hilos. 1 No obstante. y pudiéndose ajustar este valor de 16 a 134. La máquina virtual de Erlang gestiona la planificación de los procesos 1 en ejecución y eleva ese límite a 2 millones .217. No obstante. este hecho no afecta al resto de procesos que pueden seguir trabajando con normalidad. Erlang sin embargo emplea el paso de mensajes. el hecho de que la máquina virtual de Erlang se encargue de los procesos y del planificador de procesos.768. Si el proceso vuelve a levantarse y queda operativo el sistema se autorecupera del error. La ventaja principal de tener espacios de memoria exclusiva es que cuando un proceso falla y deja su memoria inconsistente. La programación concurrente se basa la compartición de datos. no todo es perfecto y siempre hay inconvenientes en las ventajas que se pintan. Aunque BEAM está bastante optimizada y el rendimiento de la máquina se ha ido incrementando en cada versión liberada de Erlang. es posible que un fallo en la memoria (que sí es compartida) afecte a más de un hilo. 66 . Por un lado.Procesos apartado concretaremos esas ventajas e inconvenientes para saber manejarlos y conocer las limitaciones y las potencias que proporcionan.727. La comunicación entre procesos es más simple y más nutrida. con el parámetro +P se puede configurar un número mayor. Esto confiere las siguientes ventajas: La limitación de procesos lanzados se amplia. o bien mediante la existencia de secciones críticas de código que manipulan los datos compartidos a través de monitores. bien mediante mecanismos como la memoria compartida y el bloqueo de la misma a través de semáforos. y es porque me atrevería a decir que es el único lenguaje que dispone de una máquina virtual sobre la que se emplean procesos propios de la máquina virtual y no del sistema operativo. por máquina virtual lanzada el límite es algo más bajo por defecto. tiene su coste. Cada proceso tiene su propia memoria y por tanto no comparte nada con el resto de procesos. e incluso al programa entero. La mayoría de sistemas operativos que se basan en procesos o hilos limitan su lanzamiento a unos 64 mil aproximadamente. Por ejemplo. format. el proceso nuevo finaliza en ese momento. en concreto una función para facilitar su compresión y uso (ya que es un BIF o función interna) llamado spawn/1. función y argumentos). siglas de Process ID o Identificador de Proceso. pasada como parámetro a spawn/1 puede realizarse de varias formas distintas. Podríamos hacer lo mismo en forma de clausura. Si el código se demorase más tiempo en ejecutarse. La primera parte la veremos un poco más adelante. Se puede emplear una clausura o indicar.0> > is_process_alive(Pid). la función is_process_alive/1 devolvería un resultado diferente. Si almacenásemos el identificador de proceso llamado comúnmente PID en una variable veríamos que el proceso ya no está activo mediante la función interna is_process_alive/1: > Pid = spawn(fun() -> io:format("hola mundo!") end). si quisiéramos ejecutar en un proceso separado la impresión de un dato por pantalla.Procesos 3. Lanzando Procesos El lanzamiento de los procesos en Erlang se realiza con una construcción del lenguaje.38. 67 . false 2 Como dijimos en su definición un proceso se mantiene vivo mientras tiene código que ejecutar. la función que se ejecutará. la llamada a la función format/1 termina en el momento en el que imprime por pantalla el texto que se le pasa como parámetro. hola mundo!<0. Ahora nos centraremos en el lanzamiento del código en el nodo actual. por lo tanto. Esta función interna se encarga de lanzar un proceso que ejecute el código pasado como parámetro. ["hola mundo!"]). obteniendo el mismo resultado: > spawn(fun() -> io:format("hola mundo!") end). a través de una tripleta de datos (módulo. Obviamente. El retorno a esta llamada es el identificador del proceso lanzado. La identificación de la función. podríamos ejecutar lo siguiente: > spawn(io. Las opciones que acepta spawn/1 se refieren sobretodo al nodo Erlang en el que se lanza el proceso y al código para ser ejecutado. junto con la configuración para lanzar el proceso. 2 PID. El registro de los nombres de procesos se realiza a través de otra función interna llamada register/2. [Dato] > end. El nombre debe de suministrarse como átomo. Tiene una sintaxis análoga a case con alguna salvedad. Esta sería la faceta social de nuestros procesos. en el que se toman los mensajes recibidos en el buzón del proceso o en caso de que esté vacío espera hasta la llegada de un nuevo mensaje. El comando que realiza esta labor es receive. En este ejemplo se puede observar la sintaxis que presenta receive: > receive > Dato -> io:format("recibido: ~p~n". Bautizando Procesos Otra de las ventajas disponibles en Erlang sobre los procesos.0> > register(timer.Procesos 4. true 5. veremos que se queda bloqueada. <0. Esto facilita mucho la programación ya que sólo necesitamos conocer el nombre de un proceso para poder acceder a él. Un ejemplo de esto sería el siguiente: > Pid = spawn(fun() -> timer:sleep(100000) end). Para que un proceso pueda recibir un mensaje debe permanecer en escucha. No es necesario que tengamos el identficador que se ha generado en un momento dado para ese proceso. Pid). debe de ser también como átomo. Esta función se encarga de realizar la asignación entre el nombre del proceso y el PID para que a partir de ese momento el sistema pueda emplear el nombre como identificador del proceso. Comunicación entre Procesos Una vez que sabemos como lanzar procesos y bautizarlos para poder localizarlos sin necesidad de conocer su identificador de proceso. Si ejecutamos esto en la consola. La consola de Erlang es también un proceso Erlang en sí. es poder darles un nombre. si escribiésemos self/0 obtendríamos su PID. 68 . y cuando se emplee.53. Esto quiere decir que debe de mantenerse en un estado especial. veamos cómo establecer una comunicación entre procesos. Esto ocurre porque el proceso está a la espera de recibir un mensaje de otro proceso. Nota Cada proceso en Erlang tiene una cola de mensajes que almacena los mensajes recibidos durante la vida del proceso. el envío debe de agregar el PID de quién envía el mensaje. Si queremos como prueba enviar información y recibir una respuesta podemos realizar lo siguiente: > Pid = spawn(fun() -> > receive > {P. Vamos a probar con un el siguiente código: > Pid = spawn(fun() -> > receive Any -> > io:format("recibido: ~p~n". el proceso hijo creado con spawn/1 se mantiene a la escucha desde el momento de su nacimiento. Para poder realizar una comunicación bidireccional.0> > Pid ! "hola". para que cuando se ejecute receive. imprime el mensaje M por pantalla y envía el mensaje adios al proceso P. un registro o una tupla con la complejidad interna que se desee. [Msg]) > end. 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 la derecha.M}. ya sea un átomo. el mensaje pueda ser desencolado y procesado. una lista.0> > Pid ! {self(). "hola"}. [M]).M} -> > io:format("recibido: ~p~n". <0.40. recibido: "hola" retorno: "adios" Con este código.Procesos El envío de un mensaje desde otro proceso se realiza a través de una construcción simple del lenguaje. <0.49. Cuando recibe una tupla con la forma {P. > P ! "adios" > end > end). La información enviada puede ser de cualquier tipo. > receive > Msg -> > io:format("retorno: ~p~n". [Any]) > end > end). 69 . Mensaje} -> 70 . A continuación se mantiene a la escucha de la respuesta que le envía el proceso hijo. > P ! "adios" > after 1000 -> > io:format("tiempo de espera agotado~n") > end > end).M} -> > io:format("recibido: ~p~n". podríamos hacerlo así: -module(escucha). o si simplemente se quiere escuchar durante un cierto período de tiempo.0> tiempo de espera agotado Si ponemos más segundos y realizamos el envío del mensaje antes de que finalice este período. Importante Las secciones de opción dentro de receive pueden tener también guards. Desarrollado en forma de módulo. queremos que el proceso que lanzamos se mantenga sólo un segundo en escucha y si no le llega ningún mensaje finalice indicando este hecho. escucha() -> receive {Desde. Si dejamos el tiempo pasar. [M]). para aprovechar la recursividad y que el proceso se mantenga siempre activo. o nunca concuerde. En caso de que el mensaje recibido no concuerde con ninguna de las opciones dadas será ignorado y se seguirá manteniendo el proceso en modo de escucha. podemos emplear la sección especial after. podemos reescribirlo de la siguiente forma: > Pid = spawn(fun() -> > receive > {P. Como opción de salida para evitar posibles bloqueos en caso de que un evento nunca llegue. En esta sección podemos indicarle al sistema un número de milisegundos a esperar antes de cesar la escucha. el comportamiento es exactamente igual al anterior. <0. en este caso adios. -compile([export_all]). en el código anterior.Procesos El proceso de la consola es quien se encarga de realizar el envío del primer mensaje hacia el proceso con identificador Pid agregando su propio identificador (obtenido mediante la función self/0) a la llamada. Si por ejemplo. pudiendo indicar un código específico en este caso. el proceso finalizará su ejecución informando por pantalla que el tiempo se ha agotado.47. El otro tipo es un simple mensaje de stop. Algo) -> Pid ! {self(). se imprime el mensaje dime algo!.0> dime algo! > escucha:dime(Pid. escucha() end. Acepta dos tipos de mensajes. "hola a todos"). Por un lado el que ya habíamos visto antes. <0. "hola"). ok. recibido: "hola a todos" ok dime algo! > escucha:para(Pid). También es la base de la mayoría de código que se escribe 71 . Además. Esta es la base sobre la que se fundamenta una de las aplicaciones más importantes de Erlang. proceso terminado Con este ejemplo queda claro que lanzar un proceso es una actividad trivial. o desde el inicio de la ejecución. cada 5 segundos desde el último mensaje enviado. una tupla {proceso. ejecutando recursivamente la función escucha/0 para seguir con el proceso activo. mensaje} que recibirá desde otro proceso que se comunica con éste (se presentará por pantalla). escucha(). recibido: "hola" ok dime algo! > escucha:dime(Pid. o desde el último tiempo agotado. la solución de problemas en entornos concurrentes. Cuando se recibe. al igual que el intercambio de mensajes entre procesos. La función escucha/0 (del módulo homónimo) se mantiene a la espera de mensajes. escucha. stop -> io:format("proceso terminado~n") after 5000 -> io:format("dime algo!~n"). Algo}. El código para utilizar este módulo podría ser algo como: > Pid = escucha:init(). init() -> spawn(escucha. el proceso finaliza su ejecución.Procesos io:format("recibido: ~p~n". []). Desde ! ok.34. [Mensaje]). dime(Pid. para(Pid) -> Pid ! stop. ok. como ya no volvemos a ejecutar la función de escucha/0. el proceso lanzado moriría pero al proceso lanzador no le sucedería absolutamente nada.42..0> =ERROR REPORT==== 27-Apr-2012::19:10:51 === Error in process <0. por lo que una vinculación de error con la misma no provoca su cierre por el error recibido.. <0.0> =ERROR REPORT==== 27-Apr-2012::19:10:51 === Error in process <0. El siguiente fragmento de código contiene un error: > Pid = spawn(fun() -> A = 5.39. ya que el proceso fue lanzado sin vinculación. el de la consola. si el código contuviera un fallo (no de compilación. aunque independientes. A continuación iremos ampliando y matizando aún más lo visto en este apartado.0> with exit value: . En este caso ambos procesos. Cambiando spawn/1 por spawn_link/1 el lanzamiento del proceso se realiza con vinculación. El error aparece en la consola provocando que el proceso termine inmediatamente.39.. Nota La consola está diseñada para procesar las excepciones. Es posible establecer una vinculación o enlace vital entre procesos de modo que si a cualquiera de ellos le sucede algo.39. Completando el ejemplo anterior. 6. 72 .Procesos en este lenguaje. ** exception exit: {case_clause. case A of 6 -> no end end). el otro es inmediatamente finalizado por el sistema.0> with exit value: . Procesos Enlazados Otra de las funcionalidades que proporciona Erlang respecto a los procesos es la capacidad para enlazarlos funcionalmente.. no le sucede absolutamente nada. sino que simplemente indica que ha recibido una excepción de salida. Al proceso principal.5} Vamos a hacer un ejemplo más completo en un módulo. case A of 6 -> no end end). sino de ejecución). ya que uno depende del otro (así se indica al lanzarlos enlazados). finalizarán. <0. produciendo: > Pid = spawn_link(fun() -> A = 5. Ni tan siquiera se entera. Tenemos dos procesos que se mantienen a la escucha por un tiempo limitado y uno de ellos en su código tiene un error. [A]).3 zape . [0]). zape(A) -> io:format("zape . Este proceso a su vez genera otro enlazado que ejecuta la función zipi/1. zipi(A+1). -compile([export_all]).4 zape .0 ok zape . Después se mantiene ejecutando la función zape/1. Revisando zape/1. lanza() -> spawn(gemelos. este proceso también finalizará su ejecución.1 zape . case A of A when A < 5 -> ok end.Procesos El código sería así: -module(gemelos). zipi.4 zipi . Como está enlazado a zipi/1. zape(A+1). [A]). zape(0). no habrá concordancia posible en la sentencia case lo que generará un error que terminará con el proceso. crea() -> spawn_link(gemelos. todos ellos enlazados.3 zipi . timer:sleep(1000).1 zipi . timer:sleep(500). timer:sleep(1000). zipi(A) -> io:format("zipi .~w~n". zipi .5 zape . Visto desde la consola: > gemelos:lanza().2 zape . el proceso que ejecuta zipi/1 y el proceso que ejecuta zape/1.6 > 73 . ok.2 zipi .5 zipi .~w~n". Al ejecutar la función lanza/0.0 zipi . crea. se genera un nuevo proceso independiente (sin enlazar). []). Tendríamos pues tres procesos: el de la consola generado por la llamada a lanza/0. podemos ver que cuando el contador llegue a 5. Zape).0> with exit value: . Volviendo sobre nuestro ejemplo anterior. ok. [0]). Zipi = spawn(gemelos. zipi(A) -> io:format("zipi . zape. Zipi).~w~n".Procesos =ERROR REPORT==== 30-Oct-2012::22:21:58 === Error in process <0. De este modo si uno de ellos finaliza su ejecución el enlace con el proceso lanzador hará que éste finalice por lo que el resto de procesos serán también finalizados en cascada. [0]). es decir. Analizando la salida.. -compile([export_all]). timer:sleep(1000).34. lanzador:agrega(LanzadorPid. Zape = spawn(gemelos. init() -> spawn(lanzador. Pid) -> Lanzador ! {link. loop(). vemos que se imprime zape por pantalla hasta que al evaluar el código se produce un error que termina ese proceso y su enlace. Pid} -> link(Pid). El parámetro que esperan ambas funciones es el PID del proceso a enlazar con el actual en el que se ejecutan. []). timer:sleep(500). Los enlaces se puede establecer o eliminar a través de las funciones link/1 y unlink/1. podemos crear un proceso que se encargue de lanzar a los otros manteniendo un enlace con cada uno de ellos. el proceso zipi. [A]). -compile([export_all]). loop. El código del lanzador podría crearse en un módulo que usara la función link/1 de esta forma: -module(lanzador). Ahora el módulo gemelos se simplifica de la siguiente forma: -module(gemelos_lanzador). lanza() -> LanzadorPid = lanzador:init(). 74 .. lanzador:agrega(LanzadorPid. LanzadorPid. agrega(Lanzador. loop() -> receive {link. zipi. Pid}. error -> throw(error) end. 58. [Any]) > end > end).#Ref<0. Monitorización de Procesos En contraposición al enlace vital. 7.process. Al enviarle hola. En otras palabras. timer:sleep(1000). [A]).58. {<0.46>} > Pid ! "hola". Si un proceso finaliza su ejecución de forma normal y satisfactoria. al enviarle el mensaje error al lanzador éste lanza una excepción produciendo la caída automática de los tres procesos. no hemos introducido un error en el código del módulo gemelos_lanzador sino que el error se produce durante el procesamiento de uno de los mensajes del lanzador.<0. debe producirse una finalización por error. Este mecanismo permite que podamos conocer si un proceso sigue activo o si ha finalizado su ejecución. En este caso. Este tipo de enlace es diferente al anterior que simplemente propaga los errores haciendo que se produzcan en todos los procesos enlazados. el enlace informativo o monitorización tal y como se conoce en Erlang.Procesos zipi(A+1). queda finalizado y desenlazado del resto de procesos pero los demás no finalizan. zape(A) -> io:format("zape . ya sea por un error o de forma normal.0.0.~w~n".#Ref<0.96>. recibido: "hola" > flush().normal} ok El primer proceso tiene un receive que lo mantiene en espera hasta que le llegue un mensaje.0. zape(A+1). Shell got {'DOWN'. el proceso que provoca la caída de los procesos en cascada debe de haber acabado con un error de ejecución. En concreto.0>. el proceso finaliza 75 .0>. Importante Para que la finalización de un proceso provoque que todos sus enlaces también finalicen. permite recibir el estado de cada proceso como mensaje.MonRef} = spawn_monitor(fun() -> receive > Any -> > io:format("recibido: ~p~n".0. para que un proceso enlazado sea finalizado por otro. Un ejemplo simple del paso de mensajes cuando un proceso finaliza se puede ver a través de este sencillo código: > {Pid. init() -> Pid = spawn(fun() -> loop([]) end). <0. Pid. MonitorRef. Name. al morir un proceso.0. el Pid se refiere al identificador del proceso que se está monitorizando y Reason es la razón de terminación. Empleando el ejemplo anterior: > Pid = spawn(fun() -> receive > Any -> > io:format("recibido: ~p~n". La función spawn_monitor/1 se encarga de lanzar el nuevo proceso y enlazarle el monitor al proceso de la consola. sea capaz de relanzarlo cuando se recibe la notificación de terminación. entre ellos el de finalización del proceso lanzado anteriormente. register(monitor.process.0. process. [Any]) > end > end).normal} ok El mensaje de finalización enviado por el proceso es una tupla que consta de las siguientes partes: {'DOWN'. Reason} La referencia.58. será debido a que encontró algún fallo.96> > Pid ! "hola". Si la razón es normal es que el proceso ha finalizado de forma correcta.96>. Shell got {'DOWN'. Pid). El uso de monitores nos puede servir para crear un lanzador como el del apartado anterior pero que.58.#Ref<0.Procesos satisfactoriamente. Cuando ejecutamos la función flush/0 podemos ver los mensajes que ha recibido la consola. en caso contrario. Fun} -> 76 . ok. loop(State) -> receive {monitor. #Ref<0. es la misma que retorna la función monitor/2. Se trata de un monitor que se puede implementar de la siguiente forma: -module(monitor).0> > monitor(process. El primer parámetro de esta función es siempre process y el segundo parámetro será el PID del proceso a monitorizar.0>. Si queremos lanzar un monitor sobre un proceso ya creado tendríamos que recurrir a la función monitor/2. MonitorRef.0.0. Pid). recibido: "hola" > flush().<0. -export([init/0. agrega/2]). From. podemos utilizar este código en consola de la siguiente forma: > monitor:init(). register(Name. AntiguoHijo = {Pid. From ! {ok. Fun) -> monitor ! {monitor. NuevoHijo = {NewPid. NewPid = lanza(Name. Hola Miguel! "Miguel" reavivando hijo en <0.Fun]}. io:format("reavivando hijo en ~p~n". State).. Pid.0> > hola_mundo ! "Miguel".. Fun) -> Pid = spawn(Fun). Fun]}|State]). 8.[Name.Fun]}. [NewPid]). hola_mundo > hola_mundo ! "Manuel". Fun).40.. ok > monitor:agrega(hola_mundo. {'DOWN'.[Name. agrega(Name. Pid). Como ejemplo.[AntiguoHijo]) end.process. fun() -> > receive > Any -> > io:format("Hola ~s!~n".! y finaliza. tal y como se observa en los mensajes reavivando hijo en . Recarga de código Uno de los requisitos con los que se desarrolló la máquina virtual de Erlang fue que el código pudiese cambiar en caliente sin afectar su funcionamiento. El proceso monitor recibe la salida del proceso y vuelve a lanzarlo de nuevo.. Pid} -> Pid end._Ref.0> El código presente en la clausura no mantiene ningún bucle. Fun] = proplists:get_value(Pid.Pid. 77 ._Reason} -> [Name. loop([NuevoHijo|State] -. receive {ok. Pid). loop([{Pid.[Name. Fun}. Name}. Name. El mecanismo para cambiar el código es parecido al que se realiza con los lenguajes de scripting con algunos matices. lanza(Name..Procesos Pid = lanza(Name.38. Fun). monitor(process. [Any]) > end > end). Cuando recibe un mensaje se ejecuta presentando por pantalla el texto Hola . Hola Manuel! "Manuel" reavivando hijo en <0. self(). de forma fully qualified. prueba:code_change(). el sistema debe de extinguir la versión más antigua del código para quedarse sólo con las dos últimas.prueba} > Pid = spawn(prueba. []). [Any]) end. Esta forma de llamar a la función le permite a la máquina virtual de Erlang revisar si hay una nueva versión del código en el fichero BEAM y. code_change. a la función code_change/0. por lo que los procesos antiguos con el código más antiguo serían eliminados.39. 78 . Si tenemos un código ejecutándose que no se llama de forma full qualified. La recarga de código afecta más a un proceso que lo emplea de forma continua (como es el código base del proceso). ok. code_change() -> loop(). recargarla. que a otro que lo emplea de forma eventual (funciones aisladas que se emplean en muchos sitios). Pero si se lanza otro proceso nuevo. init() -> loop(). La función loop/0 a su vez llama. -export([code_change/0. En ese momento habrá dos instancias diferentes de un mismo código. {ok. en caso de ser así. pero nos parece apropiado ya que la recarga de código afecta directamente a los procesos. Importante Erlang puede mantener hasta dos instancias de código en ejecución.Procesos Quizás sea un poco extraño encontrar este tema en un capítulo dedicado a procesos.0> > Pid ! "hola". aunque cambiemos el código BEAM no se recargará. se hará con la nueva versión del código. Si se volviese a modificar el código. Teniendo este código: -module(prueba). original: "hola" ok Se genera un proceso que mantiene el código de loop/0 en ejecución y atiende a cada petición que se le envía al proceso. init/0]). Pondremos un ejemplo. <0. loop() -> receive Any -> io:format("original: ~p~n". Desde una consola podemos compilar y ejecutar el código como de costumbre: > c(prueba). cambio: "hola" ok 79 . Es bueno saber que podemos hacer que la recarga de código se haga bajo demanda. loop() -> receive update -> code:purge(?MODULE). code:load_file(?MODULE). utilizando las funciones adecuadas: -module(prueba). prueba:code_change(). Vamos a la consola de nuevo y recompilamos: > c(prueba). loop() end. Una vez hecho esto podemos provocar la recarga del códig enviando el mensaje update desde consola fácilmente: > Pid ! update. ok. code_change() -> loop().prueba} > Pid ! "hola". haciendo una llamada. hasta que no provocamos una segunda ejecución no se ha producido la recarga del código ni comenzado a ejecutar el nuevo código. update > Pid ! "hola". cambio: "hola" ok Dado que el proceso está ya en ejecución. Any -> io:format("original: ~p~n".Procesos Si cambiamos el código del listado anterior por lo siguiente: loop() -> receive Any -> io:format("cambio: ~p~n". {ok. original: "hola" ok > Pid ! "hola". ok. ok. ?MODULE:code_change(). [Any]) end. Después cambiamos el código modificando el texto que imprime por pantalla el mensaje y lo compilamos con la orden erlc. -export([code_change/0]). [Any]). Para probar este ejemplo lo lanzamos como la primera vez. comprobación y eliminación. Erlang nos provee de una función llamada exit/2 que nos permite enviar mensajes de terminación a los procesos.org/doc/man/erlang. Su sintaxis por tanto es: exit(Pid. Comenzaremos por lo más básico. Los procesos aceptan estas señales y finalizan su ejecución. Reason). información de la memoria usada por el proceso (memory).Procesos Esta vez la llamada update nos ahorra el tener que hacer otra llamada adicional para que se ejecute el código nuevo. El segundo parámetro es opcional y representa el motivo de la salida. El primer parámetro es el PID que es el dato que requiere exit/2 para finalizar el proceso. la eliminación. 3 Toda esta información puede ser consultada. 10. Nodos Erlang La máquina virtual de Erlang no sólo tiene la capacidad de gestionar millones de procesos en un único nodo. sino que también facilita la comunicación entre lo que se conoce como nodos. Aunque ya hemos visto muchas de estas características como la creación. etc. nos quedan otras como el listado. por quién está siendo monitorizado (monitored_by) o a quién monitoriza (monitors). la cola de mensajes (messages). Por defecto se envía el átomo normal.erlang. o de facilitar la comunicación entre procesos a través de paso de mensajes implementado a nivel de proceso. Gestión de Procesos Como hemos dicho desde el principio. Por otro lado processes/0 nos proporciona una lista de procesos activos. por lo que posee su propia gestión de procesos. de la que ya comentamos sus ventajas e inconvenientes.html#process_info-2 80 . 9. Erlang ejecuta su código dentro de una máquina virtual. En este apartado revisaremos las características de que disponemos para la administración de procesos dentro de un programa. dando al programador la transparencia suficiente para que dos procesos comunicándose entre nodos diferentes se comporten como si estuviesen dentro del mismo. Con process_info/1 obtenemos la información sobre un proceso dado el PID e incluso mediante process_info/2 con un parámetro que indica la información específica de la lista de 3 propiedades : enlaces con otros procesos (links). el nombre del proceso (registered_name). vinculación y monitorización. con mayor detalle de la siguiente dirección: http:// www. Esta máquina virtual posee la capacidad de poder comunicarse con otros nodos siempre y cuando se cumplan unas características concretas: • El nodo se debe haber lanzado con un nombre de nodo válido. estando en la misma red. Remoto = test2@bosqueviejo. tendríamos dos nodos en estos momentos levantados: test1@bosqueviejo y test2@bosqueviejo. • La cookie debe de ser la misma en ambos nodos.Procesos Cada nodo es una instancia en ejecución de la máquina virtual de Erlang. Procesos Remotos Hasta ahora. Los nodos a los que está conectado ese nodo se obtienen con la función interna nodes/0. El nombre propio del nodo se obtiene a través de la función interna node/0. Los nodos de un cluster se obtienen con la forma: [node()|nodes()] Desde la consola podemos usar el siguiente comando para conectar los dos nodos: (test1@bosqueviejo)> [] (test1@bosqueviejo)> (test1@bosqueviejo)> true (test1@bosqueviejo)> [test2@bosqueviejo] nodes(). veremos que el símobolo de sistema de la consola de Erlang se modifica adoptando el nombre del nodo de cada uno. La cookie es una palabra de paso que permite a un nodo conectarse con otros nodos siempre que compartan la misma cookie. En caso de que el nombre de la máquina en la que ejecutamos esto fuese por ejemplo bosqueviejo. 11. desde una terminal sería el siguiente: erl -sname test1 -setcookie mitest Si lanzamos esta línea para test1 y test2. net_kernel:connect_node(Remoto). • Deben de poder conectarse. Un ejemplo de lanzamiento de un nodo Erlang. nodes(). que se ejecutaba en el nodo que 81 . cuando empleábamos la función interna spawn/1 generábamos un proceso local. a la que se conoce como cookie. Erlang dispone de un mecanismo de seguridad de conexión por clave. Los procesos en nodos remotos no se puede registrar con la función interna register/2. {set. Data). Key) -> Pid ! {get. set/3]). receive Any -> Any end. Key}. En la función init/1. son sólo accesibles desde el nodo que los lanzó. Si tenemos otros nodos conectados. a diferencia de los vistos anteriormente. From. spawn(Node. Key} -> Val = proplists:get_value(Key. Se puede lanzar un proceso remoto con la misma función spawn/1 agregando como primer parámetro el nombre del nodo donde queremos lanzar el proceso. Value}|Data]). -export([init/1. Key. se agrega el nombre del nodo que se pasa como parámetro a spawn/2. podemos realizar programación paralela o distribuida. get/2. tiene su primera parte distinta de cero lo que indica que está corriendo en otro nodo. stop -> ok end. lanzando la ejecución de los procesos en otros nodos. Value) -> Pid ! {set. Por ejemplo. 82 . Key. fun() -> loop([{"hi". loop(Data) -> receive {get. Es lo que se conoce como un proceso remoto. init(Node) -> io:format("iniciado~n"). "hola"}. get(Pid. La comunicación la podemos realizar normalmente como en todos los casos anteriores que hemos visto sin problemas. podríamos hacerlo con el siguiente código: -module(hash). "adios"}]) end). self(). Value} -> loop([{Key. Value}. es decir. {"bye". Key. si queremos lanzar un proceso que se mantenga a la escucha para dar información en el cluster montado por los dos nodos que lanzamos en el apartado anterior. No obstante. loop(Data). no se les puede asociar un nombre y por ello. el PID devuelto.Procesos corre la función. From ! Val. ok. set(Pid. Procesos 12. true (test2@bosqueviejo)> global:whereis_name(consola) ! "hola". a través del nombre. Debe usarse el módulo global con global:register_name/2. Si queremos que un proceso sea accesible desde diferentes nodos debe registrarse como proceso global. "hola" ok Registramos el proceso de la consola con el nombre consola. o incluso hasta defectuoso. obtenemos la capacidad de tener accesibilidad a los procesos remotos desde cualquier nodo del cluster. Por esta 4 razón han aparecido sustitutos como gproc (que requiere del parcheo de parte del código OTP de Erlang). 4 5 https://github.com/uwiger/gproc https://github. La accesibilidad existe desde cualquier nodo que esté conectado con el que posee el proceso. yes (test1@bosqueviejo)> receive (test1@bosqueviejo)> Any -> io:format("~p~n". En todo caso. self()). Desde el otro nodo de Erlang podemos enviar un mensaje de la siguiente forma: (test2@bosqueviejo)> Remoto = test1@bosqueviejo. [Any]) (test1@bosqueviejo)> end. El acceso a un proceso así registrado se realiza como hasta ahora. "hola" El envío del mensaje lo podemos realizar a través del PID o a través de la función send/2 del módulo global. o módulos que no 5 requieren de ninguna modificación en la base como nprocreg . El lanzamiento del proceso se realiza como hasta ahora. lo único que varía es la forma en la que se registra su nombre. Vamos a lanzar un proceso global en un nodo: (test1@bosqueviejo)> global:register_name(consola.com/nitrogen/nprocreg 83 . (test2@bosqueviejo)> net_kernel:connect_node(Remoto). Procesos Locales o Globales Todos los procesos que hemos registrado hasta ahora eran locales. Nota Hay muchos casos en los que el módulo global puede tener un rendimiento bastante bajo. 28.97.0>|.F} = code:get_object_code(hash). 6 Son las siglas de Remote Procedure Call.0>.<6759.18.24. {module.30.0>.31.126.5. erlang.29.0>. ejecutar un comando y obtener una respuesta.<6759.0>.0>.49.23.0.0>. datos como los PID se adaptan a la comunicación entre nodos.0>.<<70.0. {hash.26. A).9.25. Cualquier código que se ejecute a través de este sistema de RPC será ejecutado en el nodo que se indique como primer parámetro.<6759.17.69.<6759.20..0.0.0.11.<6759.Procesos 13.3.0>.<6759.116. F.111.0>.<6759.0>. podemos conectarnos a cada nodo remoto y obtener esta información vía RPC de la siguiente manera: (test1@bosqueviejo)> lists:map(fun(Nodo) -> (test1@bosqueviejo)> rpc:call(Nodo. es que permite conectarse a un nodo específico.27.0>.<6759.0>.]] Aunque el código se ejecuta en el otro nodo.0>. RPC: Llamada Remota a Proceso Otra de las propiedades que tiene la máquina virtual de Erlang.0>.19.6.hash} 6 De esta forma. 0.<6759.<6759.77. (test1@bosqueviejo)> Host = [email protected].<6759. si queremos obtener el identificador de los procesos de cada nodo conectado al cluster. La principal diferencia con ejecutar un proceso remotamente es que el comando RPC se lanza y se mantiene a la espera de un retorno para esa ejecución.79.15.<6759. 84 .<6759.>>.0>.0>. <6759.0>.13.0>.<6759.<6759.. existe la posibilidad de exportar el código al nodo donde queremos que se ejecute.<6759.109. Esto puede conseguirse con el siguiente código: (test1@bosqueviejo)> {hash. code.<6759..<6759. B]. Todo quedaría en llamadas RPC desde el nodo maestro hacia los demás nodos para ir levantando instancias del código y lanzar los procesos que se requieran.beam"} (test1@bosqueviejo)> A = [hash. <6759.0>.<6759.0>. En caso de que el código resida únicamente en el nodo que solicita la ejecución remota. <6759.32. "/home/bombadil/hash. (test1@bosqueviejo)> rpc:call(Host. o Llamada a Proceso Remoto.82.17. podríamos levantar cada nuevo nodo en cualquier máquina sin tener el código.0>.10. por lo que podríamos emplear cualquiera de esos identificadores para obtener información del proceso remoto.0>.66.8.0>.14. por lo que el código debe de existir en ese nodo. nodes()).B. <6759.0. [[<6759.0>.65.21..12.104. Por ejemplo.<6759. load_binary.<6759..0.4.0.0>.0>. []) (test1@bosqueviejo)> end.<6759.22.0>.65.4. <6759. processes. Estos datos pueden ser manejados a través del uso de las siguientes funciones internas: get/0. Es muy útil para limpiar completamente el diccionario. 85 . Con el parámetro clave. del módulo rpc podemos envíar el código a cada uno de los nodos conectados en el cluster. put/2 Almacena el par clave-valor pasados como parámetros. get/1 Cuando se indica sin parámetros se obtienen todos los datos contenidos dentro de ese proceso. El formato de esta devolución es una lista de propiedades que puede ser manejada con las funciones del módulo proplists. siendo el primero la clave y el segundo el valor. Cuando se indica un parámetro. Este diccionario es útil para poder desarrollar procesos en los que queramos manejar atributos. get_keys/1 Se emplea para obtener todas las claves cuyos valores son los indicados como único parámetro de la llamada a la función. 14. indicar que Erlang dispone de un diccionario de datos que puede ser empleado para mantener datos propios del proceso. erase/1 Sin parámetros se encarga de eliminar todas las ocurrencias del diccionario. Proporciona un depósito de datos por proceso que nos puede ayudar a mantener información de estado entre llamadas a un mismo proceso. Podríamos considerarlo como atributos propios del proceso. Diccionario del Proceso Para finalizar y dar por terminado este capítulo.Procesos Nota A través de multicall/3 en lugar de call/4. se encarga únicamente de eliminar el valor correspondiente a esa clave. se toma como clave y se retorna únicamente el valor solicitado. para modificar o eliminar elementos del mismo. erase/0. Los términos ya los habíamos revisado anteriormente. En este capítulo nos adentraremos en cada aspecto referente a los ficheros y las tablas ETS y DETS. mover. Otro motivo fue el proveer al desarrollador de un modo de extraer. 1. Puede además emplear tablas ETS (Erlang Term Storage) para el almacenaje en disco o. Los ficheros tienen innumerables usos. o vídeos de tipo AVI. desde escritura de logs. etc. ETS. en las que el primer elemento de la tupla actúa como clave. acceder a un directorio para su procesado.). por lo que sabemos que se trata de tuplas. ETS Las siglas ETS se refieren a Erlang Term Storage. especialmente cuando el tamaño de la información es elevado y se necesita optimizar los tiempos de acceso. como el SQL. 1 A diferencia de llamadas a sistemas. y a los mecanismos que nos da Erlang para navegar por directorios. copiar. XML o YAML. sentencias y funciones del lenguaje y no enviadas a un subsistema. a la lectura y escritura de ficheros de texto y binarios. las tablas ETS se quería que fuesen tratadas con directivas. las funciones para manejar las funcionalidades de ets se encuentran en formato de BIF. tuplas. Además.Capítulo 6. empleando directamente DETS (Disk Erlang Term Storage). el almacenaje de términos Erlang constituye una herramienta fundamental de gestión de la información con Erlang. Por todo ello. ya que en los lenguajes funcionales el tiempo de acceso a la información suele ser función logarítmica. o almacenaje de términos de Erlang. para que el uso de este sistema fuese más rápido. 86 . —François Mauriac Uno de los puntos importantes en un lenguaje de programación es la gestión de ficheros. Nos permite realizar la lectura de un fichero en texto plano formateado como datos de Erlang (listas. números. almacenar y tratar la información con los mecanismos 1 propios del lenguaje . átomos. hasta el almacenamiento o lectura de datos o configuraciones en formatos como CSV. Erlang provee funciones básicas y muy simplificadas de acceso a ficheros y directorios. DETS y Ficheros Escribir es recordar. etc. pero leer también es recordar. pasando por los contenidos multimedia: imágenes en formato PNG. La razón para crear las tablas ETS fue la de poder almacenar gran cantidad de datos con un tiempo de acceso siempre constante.). Necesitamos pues los mecanismos que nos permitan realizar todas las operaciones con ficheros (renombrar. se permiten tuplas repetidas (o duplicadas). Conjunto ordenado (ordered_set) Es igual que el tipo anterior. 1. comparadas en su conjunto con otras. Esto quiere decir que no permite a ningún otro proceso el acceso a la misma. si tenemos que almacenar términos de forma ordenada para su extracción podemos emplear un ordered_set. Los parámetros de seguridad que podemos emplear para garantizar el acceso o denegarlo. según el caso. Bolsa duplicada (duplicate_bag) Igual que la anterior. cada elemento (cada clave de cada tupla) debe de ser único a la hora de realizar la inserción dentro del conjunto. siendo el primer elemento el más pequeño. Tipos de Tablas Podemos encontrar cuatro tipos de tablas ETS dependiendo de los algoritmos empleados para la constitución de la tabla. según el grado de repetición que queramos o tengamos que permitir para los datos. Por ejemplo. A semejanza de los conjuntos como concepto matemático. DETS y Ficheros 1. es decir. su almacenaje y la extracción de datos: Conjunto (set) Es el tipo por defecto.ETS. deben de ser distintas. Acceso a las ETS Las tablas ETS son creadas por un proceso que puede hacerlo con opciones de accesibilidad que permitan su acceso por otros procesos o no. 87 . pero eliminando la restricción de que las tuplas en su conjunto y comparadas con el resto deban de ser diferentes.2. El orden interno de los elementos no está definido. son los siguientes: private Crea la ETS de ámbito privado. Dependiendo del tipo de datos que necesitemos almacenar en las tablas podremos elegir uno u otro tipo de tabla. Bolsa (bag) La bolsa elimina la restricción de que el primer elemento ya exista.1. pero en este caso los datos entrantes en el conjunto son ordenados mediante la comparación de su clave con las claves de los datos almacenados a través de los comparadores < y >. mientras que si la información que queremos almacenar puede llegar a repetirse podríamos optar por alguna de las bolsas. pero mantiene la propiedad de que las tuplas. Acceso a la tabla Se debe de especificar alguno de los tipos de accesos para la ETS vistos: public. 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 opciones para la creación de la misma. bag o duplicate_bag. Nota Una ETS mantiene.ETS. el primer parámetro de la función es empleado como identificador para poder acceder a la tabla. keypos En caso de que queramos que la clave de la ETS no sea el primer elemento de la tupla podemos agregar esta opción de la forma: {keypos. por lo que asegura que una operación de escritura sobre un objeto de una ETS. pero sólo permite la escritura para el proceso que la creó. Cada elemento de la lista corresponderá a cada una de las siguientes secciones: Tipo de tabla Se debe de especificar alguno de los tipos de ETS vistos: set. a nivel de concurrencia. a la ETS a través del identificador de la misma. named_table Si se especifica esta opción. protected o private. Pos} 88 . DETS y Ficheros protected El ámbito protegido para la ETS garantiza el acceso de lectura a todos los procesos que conozcan el identificador de la ETS. Cualquier operación de lectura sólo podrá ver el conjunto final de las modificaciones en caso de éxito (aislamiento). en caso de que sea correcta o falle lo hará de forma completa (atomicidad). 1. siempre los parámetros de aislamiento y atomicidad íntegros. tanto para lectura como escritura. ordered_set. public Garantiza el acceso a todos los procesos.3. Las opciones están en formato de lista. Esta opción está recomendada si el nivel de concurrencia de lectura/escritura de los datos almacenados en la ETS provocan excesivo tiempo de espera y fallos a consecuencia de este cuello de botella. No obstante. el proceso hijo recibiría el mensaje: {'ETS-TRANSFER'. ya que el sistema adapta internamente los datos para que las lecturas puedan emplear incluso los diferentes procesadores que pueda tener la máquina. La configuración sería: {heir. de modo que si algo le sucediese al proceso que creó la ETS. heir El sistema puede establecer un proceso hijo al que pasarle el control de la ETS. Pid. true} Activamos la concurrencia de lectura. id. las ETS mantienen un nivel de concurrencia por bloqueo completo. Concurrencia Por defecto.ETS. Esta opción es buena si el número de lecturas es mayor que el de escrituras. DETS y Ficheros Siendo Pos un número entero dentro del rango de elementos de la tupla. Al trabajar sobre datos comprimidos los procesos de búsqueda y toma 89 . es decir. FromPid. HeirData} En caso de no especificar un heredero. ya sea para leer o escribir. mientras se está trabajando con la tabla ningún otro proceso puede acceder a ella. Para activar la concurrencia en la escritura. true} La activación de la escritura sigue garantizando tanto la atomicidad como el aislamiento. vuelve a ser la penalización existente al realizar las escrituras concurrentes. a través de la opción: {read_concurrency. La nota negativa. HeirData} Y tomaría en propiedad la tabla. compressed Los datos de la ETS se comprimen para almacenarse en memoria. se debe emplear la opción siguiente: {write_concurrency. si el proceso propietario de la tabla termina su ejecución la tabla desaparece con él. bag. 255. 1. Como ejemplos de creación de ETS: > T = ets:new(prueba. podemos emplear la función match. daremos '$1' es el comodín que nos permite sacar todos los datos: 90 . bolsa Todas las opciones vistas anteriormente se pueden emplear en cualquier orden dentro de la lista de opciones. De momento. {rojo. Si queremos ver el contenido de la tabla. Un ejemplo para nuestra bolsa creada anteriormente sería: > ets:insert(bolsa. como el hecho de emplear conjuntamente la opción bag y la opción set. [named_table]). siendo el segundo parámetro un término o una lista de términos para su inserción. Esta función emplea dos parámetros: el primero es el nombre de la ETS. el sistema empleará la última leída de la lista de opciones. Esta opción es aconsejable cuando sea más importante el consumo de memoria que la velocidad de acceso. named_table]). true Con esta llamada hemos introducido un término en el que la clave es rojo dentro de la bolsa. 0}).4. Para agregar elementos podemos emplear la función insert/2 cuyo primer parámetro es el identificador de la tabla (o su nombre en caso de named_table). [set. Por ejemplo. public]) Las opciones que predominan finalmente y las que se quedarán configuradas son bag y public. 16400 > ets:new(tabla. [set. tabla > ets:new(conjunto.ETS. conjunto > ets:new(bolsa. Lectura y Escritura en ETS Una vez que tenemos creada una ETS podemos comenzar a trabajar con ella. [bag. 0. Importante Ante el uso de dos opciones que colisionen. y el segundo es el patrón que debe de cumplir el dato para ser mostrado. en este caso: ets:new(tabla. y se pueden poner tantas como se necesite. private. []). named_table]). DETS y Ficheros de datos son más costosos. 0. []). podemos emplear la función show_all/1 para listar todo su contenido.0. show_all(_Ets.255. '$1').0. Id.0.255}].255.0.0}]] Si insertamos algunos elementos más podemos ver cómo se van almacenando del mismo modo: > ets:insert(bolsa.0}. a través del uso de los patrones.ETS. [{azul.0}]. o last/1 y prev/2. -compile([export_all]). veremos como nos retorna todo lo insertado dentro de bolsa.255.Id). Match: búsquedas avanzadas En la sección anterior vimos que el listado general se podía conseguir con una forma específica de la función match. show_all(Ets) -> show_all(Ets.0.0.255.[{verde. podemos recorrer la lista utilizando la recursión.{azul. List) -> show_all(Ets. ets:first(Ets).ets:next(Ets.255}]. Si llegamos al final nos devolverá el átomo '$end_of_table'. '$1').0.0. Si ejecutamos la función main/0.0}.255}] Con el uso de las funciones first/1 y next/2. bag]).255.0. Colores). azul). nos permite mucha mayor potencia a la hora de rescatar datos de la ETS. Para extraer un elemento concreto dado el identificador de la tupla podemos emplear la función lookup/2: > ets:lookup(bolsa.0}. main() -> ets:new(bolsa.0.5. Esta función. Un ejemplo de esto se puede ver en el siguiente módulo: -module(ets_show).0.ets:lookup(Ets. 91 . si creamos una nueva ETS. show_all(Ets. ets:insert(bolsa.0. [[{rojo.[{azul. tal y como se puede ver en el listado.{azul. show_all(bolsa). true > ets:match(bolsa. [{verde. [[{rojo.255}]).{verde.0. Igualmente. DETS y Ficheros > ets:match(bolsa. [named_table. List) -> List. Colores = [{rojo. '$end_of_table'.255.0}]] Los elementos se insertan donde mejor conviene al sistema interno.Id) ++ List). 1. vamos a ver el uso de la función select/2. ya que es el único que cumple la condición de concordancia de tener en su segunda posición el valor 255. Si pusiéramos en el match lo siguiente: {'$1'. '$1'. Las variables sin importancia se pueden especificar como '$0'. 92 . DETS y Ficheros La teoría de la concordancia para las ETS se puede emplear tanto para funciones select/2.com/].. sino también una parte de guardas y la proyección (el cómo se visualizarán en el resultado). Por último.255. esta expresión de la variable sin importancia concuerda con toda la tupla al completo. . Rescatando el ejemplo anterior. La numeración sólo es relevante en caso de especificar la forma en la que se obtendrán los resultados (para las funciones como select/2). '$2'.ETS. Nota Si empleamos la función match_object/2 en lugar de match/2 se retornará siempre el objeto completo. no sólo de enviar una tupla de concordancia. Esta función nos da para las ETS la 2 El nombre de variable sin importancia es una traducción prestada del inglés don't care al que hace referencia el sitio Learn You Some Erlang [http://learnyousomeerlang. El otro tipo de átomo con significado específico para las ETS es el comodín '_'. vemos que habíamos escrito como parámetro de la función match/2 la siguiente expresión: '$1' Al no tener $1 forma de tupla. Para esto se definen dos átomos que tienen una semántica especial para 2 el gestor de las ETS. La concordancia se tendrá en cuenta sólo a nivel de elección y no a la hora de organizar los datos para su devolución.'_'. sólo recibiremos esta. Ya vimos en su momento que el signo de subrayado se utiliza para indicar que el dato en esa posición no interesa. Son las llamadas variables sin importancia y el comodín.. Requiere que se puedan identificar variables como tal o bien con el uso de comodines.. como para las funciones match/2. Como la variable sin importancia sólo la hemos situado en la primera posición. como una función más avanzada que nos da la posibilidad. Esta concordancia se basa en pasarle la información al núcleo de ETS para realizar la extracción.'_'} Veremos que extraemos el valor rojo. Comenzaremos con este ejemplo: { {'$0'. Como se puede observar el formato es igual al conocido como calculadora polaca.ETS. la operación es el primer elemento que se encuentra y los operandos los que vienen a continuación. El formato que se emplea se denomina especificaciones de concordancia y consta de una tupla de tres elementos: el primero el de la concordancia. un funcionamiento muy parecido al de la función lookup/2.'$2'.'$8'}.6. realizamos una proyección para devolver como resultado único el primer elemento de la tupla (precisamente '$0' el identificador o clave).'$3'}.'$2'. verde) 93 .'$2'. Eliminando tuplas Para eliminar una o varias tuplas. Por último.4. Volviendo a nuestro ejemplo concreto la condición que debe de cumplir la tupla es que su elemento '$1' sea mayor que cero. El lenguaje.'$4'. [{'<'. [ '$$' ] } 1. ['$0'] } He separado en cada línea cada uno de los tres parámetros que se deben enviar para cumplir con la especificación de concordancia. el segundo es el que almacena las guardas y el tercero el que se encarga de especificar la proyección de elementos. Otro ejemplo más complejo o completo se deja a la investigación del lector: { {'$1'. sino que se aceptan todas las ETS que tengan ese número de tuplas.'$1'.0} }]. se utiliza la función delete/2. “Listas” del Capítulo 2. Esta función permite eliminar una clave concreta de una ETS dada.0}. ya visto anteriormente. en el tercer elemento. DETS y Ficheros misma potencia que nos brindan las listas de compresión sobre las listas que ya vimos en Sección 1.0}]. [{'andalso'. {'=='. En la primera línea se puede ver que no se ha realizado ninguna primera criba.'$1'. El segundo parámetro contiene una operación.'$8'. ets:delete(bolsa. {'=='. 2. Si lo que queremos es eliminar una serie de objetos específicos lo podemos realizar a través de la función match_delete/2. 94 . tomar la cabecera y crear la ETS con su contenido. con una cabecera en la que se almacenan las opciones con las que fue creada. Para evitar estos problemas necesitamos un sistema que trabaje directamente con el fichero y que la robustez para haber previsto este tipo de problemas. DETS y Ficheros También cabe la posibilidad de realizar una eliminación completa de la tabla con la función delete/1. poseen un interfaz al programador muy parecido al de las ETS. la ETS se instancie de la misma forma en la que se guardó. En Erlang este sistema son las DETS. o bien el sistema puede fallar antes de que se haya podido guardar la información en el fichero. para que cuando se vuelva a leer el fichero. Reason} En el fichero que se especifica se almacena toda la ETS. DETS Las DETS son ETS que se almacenan en disco (Disk Erlang Term Storage). La función tab2file/2 tiene la siguiente forma: tab2file(Tab. a igual que en la función match/2 ya vista. La función file2tab/1 tiene la siguiente forma: file2tab(Filename) -> {ok. Las DETS pueden mantener persistencia de información mientras que las ETS no la tienen.ETS. El almacenamiento de archivos es un buen sistema para poder gestionar los datos de una ETS de manera persistente. No obstante hay que tener en mente que siempre pueden surgir problemas. tal y como se guardó. Al tratarse también de almacenaje de términos. El medio de tratamiento y almacenamiento de la información es distinto. Reason} Se encarga de leer el fichero. Tab} | {error. Esta función acepta como parámetros la ETS. Por ejemplo el tamaño de la ETS puede superar el de la memoria que se puede emplear. y el patrón de elementos a eliminar. o bien el vaciado de sus elementos con la función delete_all_objects/1.7. Filename) -> ok | {error. 1. ETS a fichero Una forma de leer la información de una ETS desde un fichero y volver a almacenarla en un fichero es a través de las funciones tab2file/2 y file2tab/1. Incluso podría ser que el fichero se corrompiera durante su escritura. porque sino el texto se nos extendería unas decenas de páginas más y no conseguiríamos abarcarlo como se merece. no sólo posee la capacidad de trabajar con términos para su almacenaje. no existe librería que permita escribir de forma ordenada los términos hacia disco.1. 95 . por lo que. El orden interno no está definido. cada elemento (cada clave de cada tupla) debe de ser única a la hora de realizar la inserción del elemento dentro del conjunto. consultas. a excepción de los conjuntos ordenados que no se incluyen de momento por no haber encontrado una forma óptima de realizar su tratamiento. el motor de base de datos que integra OTP y que viene por defecto con Erlang.ETS. Tipos de Tablas Al igual que las ETS. DETS y Ficheros El sistema DETS se suele emplear cuando se requieren almacenar con cierta persistencia información en forma de términos Erlang y cuyo fichero de almacenaje no exceda los 2 GiB de espacio. deben de ser distintas. como motor de base de datos. pero mantiene la propiedad de que las tuplas. sino que proporciona otros elementos como transacciones. Bolsa (bag) La bolsa elimina la restricción de que el identificador de la tupla sea igual. puede ser un entorno más complejo y potente que si sólo se requieren almacenar términos. se permiten tuplas repetidas (o duplicadas). comparadas en su conjunto con otras. Nota A día de hoy (en la revisión R15 de Erlang/OTP). A semejanza de los conjuntos como concepto matemático. pero eliminando la restricción de que las tuplas en su conjunto y comparadas con el resto deban de ser diferentes. Este sistema de almacenaje es el mismo que emplea Mnesia . Está pendiente y posiblemente en futuras liberaciones veamos que finalmente se agrega a esta lista el conjunto ordenado. Bolsa duplicada (duplicate_bag) Igual que la anterior. Mnesia. Repasamos los tipos que se pueden crear para las DETS: Conjunto (set) Este es el tipo por defecto. 3 En este libro no se tratará Mnesia. las DETS se pueden crear de varios tipos y comparten estos tipos con las ETS. 3 2. distribución y fragmentación de tablas. es decir. Reason} El parámetro Name será el que le dé nombre a la DETS. El valor por defecto y máximo permitido es de 32000000. Slots} El número máximo de slots que será usado. Veamos los parámetros de la función para abrir o crear una DETS: open_file(Name. {max_no_slots. 96 . Es decir. Slots} Es un ajuste de rendimiento que permite especificar en la creación de la tabla el número de claves estimado que serán almacenadas. sino que además una vez que la DETS exista. {keypos. Name} | {error. {file. El nombre debe de ser un átomo como ocurre con las ETS. Este comando no sólo sirve para crear la DETS. DETS y Ficheros 2. Si se especifica infinity. Args) -> {ok. Las opciones que se pueden agregar como segundo parámetro son las siguientes: {access. Este dato será el que se solicite en el resto de funciones para poder acceder a la DETS. el autoguardado es deshabilitado.2. siendo este último el que se toma por defecto. El valor por defecto es 256. Por defecto se toma el nombre de la DETS para la escritura del fichero. {min_no_slots. File} El nombre del fichero que se usará. La opción por defecto es 180000 (3 minutos). el tiempo en el que se realiza una sincronización entre lo que se mantiene en memoria y el disco. AutoSave} Se indica un valor entero que indica el intervalo de autoguardado de la DETS. nos permite abrirla y seguir usándola en otras ejecuciones. Crear o abrir una DETS Esta operación se realiza mediante la función open_file/2. Access} Como acceso son válidos los valores read o read_write. {auto_save.ETS. Pos} La posición dentro del término en el que se encontrará la clave de la tupla. {ok.ETS. {file. read}]). Veamos un ejemplo de apertura de un par de tablas: > dets:open_file(bolsa. [{type. las funciones info/1 e info/2. {error."bolsa.dat"}] > dets:info(bolsa. como se puede ver.bag}. [{type. {repair. boolean()} Si la DETS se mantendrá en memoria. {file_size. true | false | force} Le dice al sistema que debe ejecutar la reparación de la DETS al abrirla. al volver a ejecutar el programa. file_size). set}. {access. bag}.1}. "bolsa.enoent}} Al igual que con las ETS. Tipo} El tipo de la tabla. {size. a través de la función close/1. {type.5464}."conjunto". Importante Para no perder información. {filename.bolsa} > dets:info(bolsa). es importante que siempre cerremos la DETS de forma apropiada. se retornará el error: {error. En caso de que se indique false y se requiera reparación. {keypos. Por defecto esta característica no está activa (false). Por defecto está activa (true). {needs_repair. es seguro que se requerirá una reparación del fichero e incluso podrían llegar a perderse datos. File}} El valor force quiere decir que la reparación se llevará a cabo aunque la tabla haya sido cerrada correctamente. Si no lo hacemos. Esto quiere decir que la DETS se copia íntegramente a la memoria al momento de abrirla realizando el volcado a disco cuando se cierra.0}. La última apertura. DETS y Ficheros {ram_file.dat"}]). en caso de que no se hubiese cerrado correctamente. nos origina un error.{file_error. [{type. 5464 > dets:open_file(conjunto. Esto es debido a que se ha intentado abrir un fichero llamado conjunto que no 97 . tal y como vimos en la sección anterior. nos proporcionan información sobre la DETS abierta dado su nombre. se puede repasar el apartado de Lectura y Escritura de ETS. damos por hecho que el comportamiento de cara al programador debe de ser el mismo. EtsTab) -> ok | {error. Como todas las funciones son iguales y se pueden emplear funciones como lookup/2. no se le está capacitando para crear el fichero y por tanto se origina el error. así como el de búsquedas avanzadas y la eliminación de tuplas. next/2.ETS. a menos que colisionen con los que vienen de la DETS en cuyo caso se sobreescribirán.. Por tanto. el fichero es abierto sin problemas pero sólo la primera vez. Reason} Con el uso de esta función los datos de la DETS son volcados en la ETS. aunque se tenga almacenado todo en una DETS por la persistencia. veremos que son prácticamente iguales que las que hay disponbiles para las ETS. Por esta razón puede haber momentos en los que.. Para esto se utilizan las funciones to_ets/2 y from_ets/2. la manipulación de las entidades de memoria siempre resulta más rápido que las de disco. De ETS a DETS y viceversa Aunque ambas estructuras están optimizadas para sus respectivos trabajos. Su definición: to_ets(Name. prev/2. 2. last/1. se quiera por motivos de rendimiento utilizar una ETS para trabajar y a la hora de almacenar los datos a disco. 2. como la instancia tiene reflejado dos usos.. Si alguna de las partes cerrase el fichero. Manipulación de las DETS Si nos fijamos en las funciones disponibles para las DETS. Los datos que contenga la ETS previamente no se eliminan. Reason} 98 . Cabe destacar que es necesario haber abierto la DETS con anterioridad. delete/2. Como el acceso que se da es de sólo lectura (read). La especificación de la función from_ets/2 es: from_ets(Name. EtsTab) -> EtsTab | {error. volver a trabajar con una DETS.3. delete_all_objects/1. . se mantiene abierta hasta que la otra parte también cierre el fichero. Importante Si se intenta abrir el mismo fichero desde dos partes diferentes del código con los mismos parámetros. first/1. el segundo usa esta primera instancia. DETS y Ficheros existe.4. es usado para crear el fichero. no tienen ningún significado especial. Modes) -> {ok. escritura (write) o para agregación al final (append). 3. salto de línea. tienen un tamaño definido y todos sus bytes son iguales. audio. el orden en el que se guardan los elementos es indeterminado. los ficheros binarios. imágenes. Ficheros En este apartado revisaremos lo que se puede hacer desde Erlang con los ficheros. 3. El caso de exclusive. 99 .1. Para ello. devolviendo un error en caso de que ya exista. DETS y Ficheros En este caso. ya que se interpretan algunos caracteres especiales como fin de fichero. aunque se pueden agrupar para definir formas de datos estructuradas. Reason} Como primer parámetro tenemos el nombre del fichero (Filename). vamos a diferenciar el tratamiento de los ficheros entre los dos tipos existentes: binarios y de texto. En cambio. y el segundo parámtro corresponde a una lista en la que se pueden agregar tantas opciones de las siguientes como se necesite: read | write | append | exclusive El fichero se puede abrir en modo de lectura (read). e incluso se pueden tomar ficheros formateados de cierta forma para que se carguen como formas de datos de Erlang. documentos formateados como los RTF. La función de que disponemos para poder abrir o crear ficheros es la siguiente: open(Filename. vídeo. Abriendo y Cerrando Ficheros Cualquier fichero que haya en un sistema de ficheros al que tenga acceso Erlang es susceptible de poder ser abierto. IoDevice} | {error. También podemos generar nuevos ficheros dentro de ese mismo sistema de ficheros. es decir. tanto de DETS a ETS. Nota En ambos casos. la DETS sí es vaciada (se eliminan todos sus elementos) y a continuación se inserta la ETS tal cual dentro de la DETS.ETS. o ficheros binarios de cualquier otro tipo. como en el caso opuesto. Los ficheros de texto tienen un tratamiento especial. Los ficheros que podemos abrir o crear son ficheros que pueden contener texto. La codificación por defecto es latin1. {error. {encoding. 100 . Sin embargo. [write]). entonces se escriben a disco. Las tablas de codificación permitidas se pueden revisar en la documentación 4 oficial de la función open/2 . por lo tanto. Esta opción puede combinarse con read o write. Size.52. [read]). este modo de trabajo tiene limitaciones. intentar incrementar el rendimiento del sistema. DETS y Ficheros raw Abre el fichero mucho más rápido. para lectura o escritura y el resultado obtenido: > file:open("/etc/debian_version". Encoding} Realiza la conversión automática de caracteres para y desde un tipo específico. {delayed_write.eacces} 4 5 5 http://www. o alguna distribución derivada de estas. Delay} Los datos se mantienen en un buffer hasta que se alcanza el tamaño indicado por Size o hasta que el dato más antiguo en el buffer es de más allá del tiempo especificado en Delay. es frecuente encontrar el fichero /etc/debian_version en el sistema de ficheros.html#open-2 Para los que usan Debian o Ubuntu.<0.erlang. {read_ahead. decrementa el número de llamadas al sistema para acceso a disco.org/doc/man/file. binary Las operaciones de lectura retornarán listas binarias en lugar de listas. Size} Activa el buffer de lectura para las operaciones de lectura que son inferiores al tamaño definido en Size. {ok.0>} > file:open("/etc/debian_version".ETS. Un ejemplo de apertura de un fichero tan famoso como /etc/ debian_version. Igual que en el caso anterior. por lo que aumenta el rendimiento. ya que ningún proceso de Erlang se encarga de manejar el fichero. como que las funciones del módulo io no pueden ser empleadas o que sólo el proceso que haya abierto el fichero puede utilizarlo. pero no ambas. Esta opción se emplea para decrementar los accesos a disco y. compressed Crea o abre ficheros comprimidos con gzip. en caso de ir seguido al avance de línea. 3. sería hacer lo siguiente: > {ok. como se ve en el siguiente ejemplo: 6 Son las estructuras del sistema operativo que se emplean para designar que un programa tiene un fichero abierto. ya que un usuario normal no tiene permisos para escribir en ese fichero.ETS.0>} > file:close(Pid). es tomado como un salto de línea y el caracter de retorno de carro (\r). ya que cada fichero abierto de un modo no raw lleva asociado un proceso Erlang. ok Importante Es importante que cerremos todos los ficheros que abramos ya que esto repercute. {ok.34. Para cerrar el fichero. Al intentar abrir un fichero para escritura hemos obtenido un error de acceso (eaccess) debido a la falta de permisos. no sólo en un uso innecesario de los recursos 6 de los descriptores de ficheros .2. Como el fichero que vimos en el ejemplo de apertura de ficheros es de tipo texto. debemos de emplear la función close/1. Leen tantos bytes como sean necesarios hasta llegar a un salto de línea o el final del fichero. sino también de procesos. DETS y Ficheros Nota Vemos que el retorno de la primera operación que se realiza correctamente. es ignorado y tomado como parte del salto de línea. Con esta consideración. 101 . podemos ir leyendo línea a línea mediante la función read_line/1 hasta el fin de fichero y luego cerrarlo. En los ejemplos anteriores. se encargan de leer una línea del fichero. nos devuelve un PID. El caracter de avance de línea (\n). Pid} = file:open("/etc/debian_version". funciones como read_line/1. Al no haber empleado la opción raw se crea un proceso Erlang intermedio que se encarga de la información del fichero y de realizar los accesos de lectura y escritura.<0. Lectura de Ficheros de Texto Los ficheros de texto son los que el sistema interpreta dando un significado concreto a ciertos caracteres especiales. y con ello liberar el proceso que se mantiene a la espera de indicaciones para tratar dicho fichero. [read]). ok Si lo que queremos es leer todo el contenido del fichero para almacenarlo en una variable de texto.ETS.34. {ok. {ok. {ok. Pid} = file:open("/etc/motd"."individual files in /usr/share/doc/*/copyright.\n"} > file:read_line(Pid).cfg) desde consola: > file:consult("datos_personales.\n"} > file:read_line(Pid). podemos emplear io:get_line/2 para realizar la lectura.0>} > file:read_line(Pid). Esta función puede leer un fichero como el que se muestra a continuación: {nombre. {ok.1. {ok."permitted by applicable law.<0. separados por un punto cada uno de los elementos base que conforman el documento. Leyendo este fichero (datos_personales.."Linux barbol 3."\n"} > file:read_line(Pid). "Jimenez"}.."the exact distribution terms for each program are.."} > file:read_line(Pid). [read])."\n"} > file:read_line(Pid). {ok.. {ok.<<"wheezy/sid\n">>} Nota Al igual que hemos empleado read_line/1."} > file:read_line(Pid). como términos o listas.cfg"). Por último. podemos emplear la función read_file/1: > file:read_file("/etc/debian_version")."The programs included with the Debian GNU/Linux. {ok.. este puede ser leído y evaluado como datos Erlang directamente. "Rubio"}.. {ok. 102 . pasando como primer parámetro el identificador del fichero abierto.0-1-amd64 Tue Jan 10 05:01:58 UTC. {ok.. si el contenido del fichero que queremos leer contiene elementos de Erlang.. "Manuel"}."} > file:read_line(Pid)."} > file:read_line(Pid). DETS y Ficheros > {ok. {apellido2. eof > file:close(Pid)."Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY. Esto se realiza con al función consult/1. {apellido1. 2f~nTotal: ~6. {apellido1."Jimenez"}]} Con esta sintaxis crearemos archivos que podamos emplear como configuración.20. podemos simplificar empleando la función write/2 tal y como se ve en este ejemplo: > {ok. {apellido2. primero una y después la otra.[12. puede ser más fácil formatearlo a través de la función format/3. Lectura de Ficheros Binarios La lectura de estos archivos la realizaremos expresamente con la función read/2. Pid} = file:open("mifile."~nSaldo: ~6.0>} > file:write(Pid.txt". 3. {ok.42. formato de números). [write]).42.<0. tal y como hacíamos con la pantalla.4. ok También es posible utilizar ambos métodos realizando escrituras combinadas. [write]). la segunda nos permite escribir sólo lo que contenga la lista de caracteres que enviamos como segundo parámetro. La primera función nos permite formatear el texto que será escrito en el fichero tal y como se haría en la pantalla.1]). tantas veces como queramos o necesitemos.2f~n". cuyos datos podemos rescatar en cualquier momento.txt".ETS. teniendo en cuenta los saltos de línea y los espacios.0>} > io:format(Pid.3."Rubio"}. Escritura de Ficheros de Texto La escritura de ficheros de texto la podemos realizar simplemente a través de las funciones format/3 o write/2. parametrización o salvaguarda de información.[{nombre. ok > file:close(Pid). Como primer parámetro emplearemos el identificador para el 103 . ok > file:close(Pid). {ok. saltos de línea. Si nuestro fichero debe disponer de un formato específico (espacios. DETS y Ficheros {ok. es decir. Pid} = file:open("mifile. por ejemplo así: > {ok.3. ok Si por contra lo que queremos es simplemente escribir un texto ya almacenado dentro de una cadena.<0."Manuel"}. 3. "fichero de texto"). 34.10>>} > {ok. [Width.78. Si queremos leer la totalidad del fichero es más aconsejable emplear la función read_file/1.0>} > {ok.48. el tamaño del fichero que será leído.10.org/wiki/Portable_Network_Graphics 104 .png" > {ok.10.0. lectura y cierre del fichero en una sola función.34.71. ok Los bytes leídos de nuestro fichero binario imagen se muestran como una lista binaria de enteros.13. [read.0.0.80.68. En concreto hemos leído 16 bytes del principio del archivo PNG.<<0. Aunque podemos emplear otras funciones.13.73.0>} > file:read(Pid.0.0. {ok.binary]). Length).0.26.<0.<<0.82>>} > file:close(Pid). "IHDR">>} = file:read(Pid.13.0. Image 48x48 pixels ok > file:close(Pid).0>>} > io:format("Image ~bx~b pixels~n".26. "/usr/share/pixmaps/debian-logo.wikipedia.Height]).26."PNG".10.13. [read. Color:8.10. como segundo parámetro.73. Podemos leer 8 bytes que conforman la firma del PNG y los 8 que contienen la cabecera del PNG en la siguiente forma (como 7 podemos ver en la wikipedia ): > Filename = "/usr/share/pixmaps/debian-logo. Es aconsejable realizar el tratamiento de ficheros binarios siempre a través de listas binarias. gracias a las listas binarias y al tratamiento que se puede realizar con los bits. En el ejemplo de la lectura del PNG hemos 7 http://en.png".71.6. {ok. <<Length:32.<<137.png" > {ok.82>>} > {ok. ya que nos auna la apertura.8.0. Pid} = file:open(Filename. Pid} = file:open(Filename.binary]). Un ejemplo de lectura de una imagen sería la siguiente: > Filename = "/usr/share/pixmaps/debian-logo.png". el tamaño de la imagen y muchos otros datos que podríamos también pasar por pantalla. ok Siguiendo las directrices de la especificación de los ficheros PNG.0.10>>} = file:read(Pid.72.78. {ok.<0. {ok.ETS. "/usr/share/pixmaps/debian-logo. Depth:8. 16). Height:32. Filter:8. DETS y Ficheros fichero abierto y.13.68.<<137.0.48. {ok. > Compression:8.72. esta es la más genérica y nos permitirá realizar las lecturas de los ficheros sin problemas. 8). 8).0.0. hemos podido extraer. <<137.80. <<Width:32. {ok. Interlace:8>>} > = file:read(Pid. 0.>>} 2> {ok.48.<0.13. la apertura del fichero debería agregar el parámetro read además de write.. sería la copia de un fichero.0.png". {ok. ok 4> file:close(Destino).0.0. A continuación hacemos una lectura completa del fichero con la función read_file/1: > {ok.10.0. {ok. Contenido} = file:read_file("logo. Si lo que quisiéramos es modificar un archivo binario (en nuestro ejemplo es modificar la cabecera PNG).6.0. o begin of file). ok 3. Los parámetros que podemos emplear son: 105 .8.26. 3.0. o para ficheros de vídeo como los AVI o MOV.0>} 3> file:write(Destino.[read.binary]). Escritura de Ficheros Binarios La escritura de ficheros binarios se realiza con la función write/2 igual que hemos hecho con los archivos de texto. tal y como se ve en este ejemplo: file:open("file.48. también llamado bof. La función que permite realizar este tipo de movimientos por el fichero es position/2 (del módulo file).binary]) Para modificar una parte específica del fichero. DETS y Ficheros visto cómo se desempaqueta el entramado de bytes para obtener la información codificada en el fichero.71.73.35. La diferencia radica en que el parámetro que se envía para ser escrito no es una lista de caracteres. habrá que desplazar el puntero al punto exacto donde queremos escribir.68..82. [read.<<137.binary]).5. Acceso aleatorio de Ficheros Hasta ahora hemos eliminado el contenido de los archivos que hemos escrito.write.0.ETS. relativas a la posición actual: cur.78.72..13. Esto es lo que se conoce como escrituras y/o lecturas aleatorias (o no secuenciales). Teniendo la definición de otros tipos de documentos binarios se podría hacer lo mismo para ficheros de audio como los WAV o MP3. sino una lista binaria.png".0.80. 0.bin". o relativas al final del fichero: eof.6. Esta función nos permite desplazarnos por el fichero a posiciones absolutas (o relativas al principio del fichero.10. El ejemplo más simple. Destino} = file:open("logo2. Contenido). [write. Abrimos el fichero que queremos leer y el que queremos escribir para lectura y escritura respectivamente. 1024).1024} > file:position(Pid. {ok. {cur."PNG">>}. {ok. Como las lecturas aleatorias son lentas. eof). {ok. 0}. {{bof. es decir.1694} > file:position(Pid.1718} > file:position(Pid. Estas funciones son pwrite/2 y pread/2.1000} > file:position(Pid. <<0. {ok.0. {eof. 24}). Estas anotaciones de cambios pueden ser del tipo: [ {{bof. {ok. Lecturas y Escrituras por Lotes Erlang tiene muchos mecanismos para optimizar al máximo posible las operaciones que se realicen con él. para pread/2. Lo que ocurre es que trabajamos en memoria almacenando un log de cambios y los pasamos al fichero de una vez cada cierto tiempo. {ok. el sistema interno de Erlang nos facilita esta labor proporcionando un par de funciones para realizar estas lecturas o escrituras por lotes. Esto se pasaría como segundo parámetro en la función pwrite/2. [binary. sobretodo si hay que llamar a la misma función una y otra vez con desplazamientos del puntero del fichero.0>} > file:position(Pid. debido a que hemos abierto el fichero para escritura y se nos permite agregar más información para ampliar el tamaño del fichero.read]).png". {ok. Estas funciones están pensadas para cuando tenemos la información en un fichero pero al mismo tiempo en memoria. {eof.0} > file:close(Pid). {{eof. DETS y Ficheros > {ok. 12}. -24}. 3.7. -24}).write. Pid} = file:open("logo.ETS. ok Vemos que incluso podemos desplazarnos más allá del tamaño del fichero (que en ese caso es de 1718 bytes).0>>}. <<137. también se puede dar la lista de direcciones para realizar las lecturas oportunas. -24}). <<"IHDR">>} ] Con esta secuencia indicamos la posición y lo que deseamos escribir en cada punto.0. De forma análoga. bof).99. de la siguiente forma: 106 .1742} > file:position(Pid.<0. Nombre del fichero Doy por supuesto que todos conocemos que. hay que tener especial cuidado con los caracteres de UTF-8. su nombre raíz (sin extensión) y/o su extensión. Gestión de Ficheros Además de todo lo visto anteriormente para la creación. 4}. DETS y Ficheros [ {{bof.png" > filename:basename(Filename). 8 propietario .png" > filename:rootname(Filename). "/home/bombadil/logo. los nombres de los ficheros se componen por la ruta en la que se ubica el fichero. 4. "/home/bombadil/logo" > filename:dirname(Filename). 4. {{bof. el módulo filename nos permite obtener la información correspondiente a un nombre de fichero: su ruta. disponemos de varias funciones: > Filename = "/home/bombadil/logo. especificamos el tamaño para leer a partir de la posición dada. 8 El cambio de permisos y propietario depende de cada sistema operativo y los permisos en sí que tenga el usuario que lanzó la ejecución del programa. truncarlo o eliminarlo.1. ya que algunos emplean dos bytes para su almacenaje en lugar de sólo uno y esto puede provocar que el cómputo de la posición sea erróneo (o susceptible de errores). como puede ser: renombrarlos. {{eof. su nombre. Para evitar estos errores. su nombre y extensión. Importante En caso de emplear listas de caracteres.png". aconsejo emplear el acceso aleatorio tan sólo en ficheros binarios. copiar el fichero. ya que las unidades están mejor definidas en este caso. 4}. Para esto. modificación y lectura de un fichero. cambiar sus permisos. "logo. -24}. 107 . En Erlang. "/home/bombadil" > filename:extension(Filename). en lugar de especificar contenido para escribir.0}. 4} ] En este caso. podemos realizar más acciones aún con estos ficheros. 12}.ETS. "logo2. {ok. completado con la ruta de trabajo actual: > filename:absname("logo. Un ejemplo de cómo podemos emplear estas funciones: > file:copy("logo. DETS y Ficheros ". nos puede servir para cambiar la ubicación del fichero si indicamos una ruta distinta.1718} > file:rename("logo2.ETS. ok > file:delete("milogo. Mover y Eliminar Ficheros Una de las acciones básicas cuando se trata con ficheros son estas tres que encabezan la sección actual. ok La función de rename/2.png"). sino que son interpretados como parte del nombre del fichero. Para estas acciones Erlang provee de tres funciones: copy/2. En caso de que no queramos eliminar un fichero (porque un programa lo tenga abierto o por otro motivo) y sólo queramos eliminar su contenido 108 . por ejemplo: file:rename("logo. "/home/bombadil/logo.png").png".png".png" Como en otros lenguajes basename/1 nos retorna el nombre del fichero sin ruta.png" 4.png"). dirname/1 nos devuelve la ruta sin el nombre del fichero.2. además de para cambiar el nombre del fichero.png"). Si le pasamos una ruta relativa o un fichero sin ruta.png") Nota Las operaciones se realizan sobre ficheros específicos. "/tmp/logo.png". no sobre grupos de ficheros como los comandos de consola de los sistemas operativos. obtenemos un nombre de fichero absoluto. Copiar. También tenemos rootname/1 que nos retorna el nombre del fichero (con ruta si dispone de ella) y extension/1 que nos da únicamente la extensión (con el punto incluído). por lo que el uso de comodines como asterisco (*) o interrogante (?) no se tienen en cuenta como tal. rename/2 y delete/1. "milogo. También disponemos de la función absname/1 que retorna siempre el nombre del fichero de forma absoluta. tendremos que ejecutar: file:change_mode("logo. Los parámetros de UID y GID se dan en formato entero .3. DETS y Ficheros y reiniciar sus punteros a la posición cero. 8#00644) El cambio de propietario se puede realizar a través de la función change_owner/2 o change_owner/3 si además queremos cambiar el 9 grupo. 109 . Permisos. tenemos esta tabla de permisos: Valor numérico 8#00400 8#00200 8#00100 8#00040 8#00020 8#00010 8#00004 8#00002 8#00001 Permiso Lectura Escritura Ejecución Lectura Escritura Ejecución Lectura Escritura Ejecución Usuario Propietario Propietario Propietario Grupo Grupo Grupo Otros Otros Otros Por lo que si queremos que el fichero logo. La función change_mode/2 permite cambiar los permisos del fichero. El cambio de los permisos se puede realizar mediante la función change_mode/2. son sus permisos y su pertenencia a un usuario o grupo. grupos y permisos y permite realizar 9 En los sistemas de tipo Unix este dato se puede ver en /etc/passwd donde hay una correspondencia entre el nombre del usuario y su UID. Propietarios y Grupos Otro de los aspectos relevantes cuando gestionamos ficheros. Hay una función que engloba todas las funciones del módulo file para la gestión de usuarios.ETS. 4. El cambio de propietario se hace mediante change_owner/2 y el cambio de grupo a través de change_group/2.png tenga permisos de lectura y escritura para su propietario y lectura para el grupo y otros. Como primer parámetro se pasa el nombre del fichero y como segundo parámetro el modo que se desea establecer. en modo numérico. esto lo podemos realizar mediante el uso de la función truncate/1.png". En modo octal. Gestión de Directorios Hasta el momento hemos visto como trabajar con ficheros. Por ejemplo: > file:read_file_info("logo. de modo que el acceso a cada directorio sea más rápido que en el caso de tener un directorio con miles de ficheros.18}. mtime={{2012. mode=33188. fecha y hora de modificación y fecha y hora del último acceso. su contenido ya sea de tipo texto o de tipo binario. access=read_write. ahora vamos a tratar la gestión de los directorios.1}}. minor_device=0.23.{14. un registro de tipo file_info y rellenarlo con los datos del fichero que deseemos modificar.type=regular.7. DETS y Ficheros todas las modificaciones en una sola acción. se debe de especificar. tipo de fichero y tamaño del mismo. así como la gestión propia de los ficheros (copia.1}}. un sistema que cree muchos ficheros puede particionar estos en directorios y subdirectorios.gid=1000}} write_file_info/2 Permite modificar cualquiera de los datos del fichero. como segundo parámetro. inode=11150880. . ctime={{2012.23. Para sistemas que trabajan con miles de ficheros esto no es una opción sino una necesidad.. Como la gestión de los directorios. 110 . Vamos a verlas un poco más en detalle: read_file_info/1 Permite leer las propiedades de un fichero retornando un registro en el que aparecen datos como la fecha y hora de creación.ETS.#file_info{size=1718. pero esto no es nada aconsejable. renombrado.1}}.{14. además de los permisos.uid=1000.).{14.23. {ok. 10 Hay sistemas de ficheros que establecen este límite a 1024 y otros que permiten miles o millones de ficheros por directorio. 5.7. como para la escritura: write_file_info/2. tanto para la lectura: read_file_info/1.png". Los directorios nos permiten organizar nuestros ficheros de una forma más categorizada.7.size).major_device=2049.18}. ya que. en el terreno informático no hay recursos infinitos e incluso el número de ficheros que pueden 10 albergarse en un directorio está limitado . eliminación.. e incluso la de los ficheros se puede realizar desde un programa.18}. ya que el tratamiento y gestión del propio directorio o de los propios ficheros puede ser extremadamente lento. atime={{2012. para ello.links=1. Un ejemplo: file:make_dir("/home/bombadil/logos") En sistemas como la shell de los sistemas tipo Unix. La ruta de trabajo podemos extraerla con get_cwd/0 del módulo file.ETS. La ruta absoluta nos indica dónde se encuentra un fichero mientras que la ruta relativa se basa en la ruta activa en la que se esté trabajando. Esta función debe de recibir una ruta existente y 111 . %% ejecuta_codigo. file:set_cwd(Dir).1. Directorio de Trabajo Las rutas que indicamos para los nombres de ficheros las podemos indicar de forma absoluta o relativa tanto para su apertura como para su gestión. se permite realizar el comando: mkdir -p /miruta/nuevo1/nuevo2/midir Con lo que no sólo se crea un directorio. Esto no lo realiza make_dir/1. file:set_cwd("/miruta"). Erlang establece una ruta de trabajo que puede ir cambiando a través de llamadas específicas al sistema. Podemos cambiar la ruta de trabajo mediante la función set_cwd/1. algo como esto: Dir = file:get_cwd(). La creación se puede indicar de forma absoluta o relativa. Esto afectará a todas las rutas relativas que se empleen a partir del cambio. Comenzaremos con el primer caso. DETS y Ficheros Veremos a continuación las funciones relativas a la gestión de directorios bajo los conceptos en los que se emplean. 5. la creación... donde especificamos cuál será la nueva ruta de trabajo. 5. Creación y Eliminación de Directorios Una de las acciones básicas con los directorios es la de su creación y eliminación. sino todo los necesarios hasta llegar al último indicado en la ruta pasada como parámetro. La ruta inicial se sitúa en el directorio en el que nos encontrásemos al ejecutar la consola de Erlang. Es un método bastante frecuente el cambiar la ruta para la ejecución de un código específico y volver inmediatamente a la ruta anterior. Cualquier referencia a fichero que hagamos de forma relativa será siempre relativa a esta ruta.2. 3. Esta función crea todos los directorios necesarios para que la ruta exista. por ejemplo. ya que la función está creada con la idea de que se pueda pasar como parámetro la ruta de un fichero (con el nombre del fichero incluído) y cree el directorio para albergar al fichero: filelib:ensure_dir("/tmp/midir/logo. crea ambos directorios. Importante El parámetro de ensure_dir/1 debe de terminar en barra para que cree hasta el último directorio. ¿Es un fichero? Para emplear en las guardas (o guards). primero prueba y después dir1 dentro de prueba. Podemos ver un ejemplo: > file:make_dir("/tmp/prueba/dir1"). En caso de que quisiéramos crear un directorio con todos sus directorios padres.ETS. {error. en caso de las configuraciones en las que se nos proporciona una ruta: temp_dir_config(Dir) when not is_dir(Dir) -> ok = filelib:ensure_dir(Dir ++ "/"). siempre que no exista ya. Por este motivo la función make_dir/1 no puede crear el directorio final. al igual que disponemos de las funciones is_list/1. 112 . en caso de que no existan. dir1. ok Partimos de que /tmp/prueba no existe.enoent} > filelib:ensure_dir("/tmp/prueba/dir1/").png") 5. temp_dir_config(Dir). podemos emplear la función ensure_dir/1 del módulo filelib. La función ensure_dir/1. Esto nos permite realizar funciones que nos permitan realizar un proceso previo de validación. Igualmente podemos emplear las funciones file_size/1 (tamaño del fichero) o last_modified/1 (la última fecha de modificación) para agregar más semántica o funcionalidad al código. podemos hacer uso de las funciones del módulo filelib: is_dir/1 o is_file/1. temp_dir_config(Dir) -> to_do. DETS y Ficheros creará el último directorio que se indique en la ruta. filelib:wildcard("/home/bombadil/*. DETS y Ficheros 5."bayadeoro"]} Otra forma de obtener los ficheros que nos interesan que podemos encontrar dentro del módulo filelib es a través de la función wildcard/1. /home/bombadil/logo. Contenido de los Directorios Hay momentos en los que queremos tener un listado de todos los ficheros que se encuentran dentro de un directorio. Una forma rápida de obtener los ficheros que queremos es emplear la función list_dir/1.png 1718 Esto nos abre una cantidad de posibilidades para obtener información de los ficheros albergados en un directorio. Por ejemplo. en las que poder emplear las guardas vistas referentes a los ficheros.png")).png"). podemos realizar un procesado individualizado de los ficheros que nos retornen las funciones.ETS. para realizar un listado dentro del programa ya sea para tener un control del número de ficheros que se van generando. no sólo poner una ruta.4. [X. para realizar búsquedas o por cualquier otro motivo. ["/home/bombadil/logo. si queremos extraer. sino además emplear los comodines para obtener los ficheros que concuerden: > filelib:wildcard("/home/bombadil/*. 113 .png"] Con estos listados.filelib:file_size(X)]) > end. o incluso para poder recorrer directorios a través de funciones recursivas. además del nombre del fichero el tamaño y mostrarlo: > lists:map(fun(X) -> > io:format("~-30s ~w~n".["bombadil". la cual nos permite. {ok. lo cual nos retorna una lista de todos los nombres de ficheros que se encuentran en la ruta pasada como parámetro: > file:list_dir("/home"). a través de funciones sobre listas como map/2. es establecer puertos de comunicación para recibir conexiones entrantes. —Herbert Marshall Mcluhan Uno de los principales cometidos de un servidor. TCP y UDP para la pila de conexiones más popular: TCP/IP. sólo nos importa el protocolo IP. Este protocolo proporciona una dirección dentro de una red y permite establecer una comunicación a través de diversos dispositivos hasta encontrar la dirección de la máquina que debe recibir el mensaje. son: Nivel físico En este nivel se encuentran las conexiones físicas y sus protocolos específicos. 802. Nivel de transporte Este nivel es el que establece la forma de conexión entre las máquinas. Nivel de red Es el nivel en el que se establece la base de la red. En este nivel y en el alcance que nos hemos propuesto para este libro. etc. todo es ahora. 1.11. El nivel físico siempre se emplea punto a punto. la forma en la que se envían y trocean los paquetes para 114 . en el modelo de Internet denominado TCP/IP. A través de los drivers (o módulos del kernel) estos protocolos son transparentes para las aplicaciones. cada máquina se conecta a través de un cable o de forma inalámbrica con otra y establecen una comunicación uno a uno. empleando en cada uno de los niveles un protocolo específico para la comunicación. La comunicación se establece a varios niveles. Las capas que se distinguen en el envío de información de un punto a otro. la identificación de los sistemas y el transporte hacia los mismos. Conceptos básicos de Redes Cuando se establece una conexión. la información que se percibe en el más alto nivel (o nivel de aplicación). En este capítulo nos centraremos en el protocolo IP. Comunicaciones No hay lugares remotos. Fibre Channel. es una representación que se ha ido resolviendo de un sistema de empaquetado anterior. según la tecnología en uso: Ethernet. En virtud de los medios de comunicación actuales.Capítulo 7. encargado de agregar información sobre el paquete y enviarlo a través de la red. tanto para conexiones cliente.1.12. Los ceros iniciales. Hay tres clases de direcciones IP que una organización puede recibir de parte de la Internet Corporation for Assigned Names and Numbers (ICANN): clase A. 1 El número binario de 8 bits más alto es 11111111 y esos bits. 1 El valor decimal de cada octeto puede estar entre 0 y 255 . clase B y clase C.65 Importante Las direcciones IP en Erlang se emplean a través de un formato de tupla formada por cuatro elementos enteros. Hewlett Packard. de derecha a izquierda. 64 y 128. Se otorgan direcciones de clase C para todos los demás solicitantes.0. salvo algunas excepciones. Cada uno de estos octetos puede estar comprendido entre 0 y 255.0. 1. Aquí se definen y usan protocolos como: HTTP. 115 . En la expresión de direcciones IPv4 en decimal se separa cada octeto por un punto. La función inet:ip/1 nos permite realizar la conversión del formato de texto al formato de tupla. como para servidor: {127. si los hubiera. Cada clase de red permite una cantidad fija de equipos (hosts). ICANN reserva 2 las direcciones de clase A para los gobiernos de todo el mundo y las direcciones de clase B para las medianas empresas. 32. Ejemplo de representación de dirección IP: 164. se pueden obviar. FTP.1} Este sería el formato de IP para 127.1. 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. por ejemplo. Esta forma es en la que generalmente trabaja el módulo inet que es el que se encarga de las comunicaciones.0. Direcciones IP Una dirección IP se representa mediante un número binario de 32 bits (según IPv4). lo que suma 255 en total. 2. IMAP.Comunicaciones que lleguen a su destino y los acuses de recibo para asegurar de que el paquete es recibido correctamente. En la actualidad. 4.123. Nivel de aplicación Este es el nivel más alto que podemos encontrar en comunicación. 8. tienen valores decimales de 1. En este nivel veremos dos protocolos: TCP y UDP. SMTP. etc. 2 Aunque en el pasado se le hayan otorgado a empresas de gran envergadura como. 16. POP3.0. x se reservan para pruebas de retroalimentación. reservando los tres últimos octetos (24 bits) para que sean asignados a los hosts.0 es utilizada por las máquinas cuando están arrancando o no se les ha asignado dirección.0. de modo que la cantidad máxima de equipos es 28 (menos dos). • En una red de clase C. pero sí se pueden repetir en dos redes privadas que no tengan conexión entre sí directamente.0. • La dirección que tiene a cero su parte destinada a equipos sirve para definir la red en la que se ubica. es decir. se asigna el primer octeto para identificar la red.0.Comunicaciones • En una red de clase A.255.0. 16. Las direcciones privadas son: • Clase A: 10.255. se asignan los tres primeros octetos para identificar la red.777.255.0 a 172. 24 bits equipos) • Clase B: 172. reservando los dos octetos finales (16 bits) para que sean asignados a los equipos.0 a 10.255. Se denomina dirección de broadcast.0. los bancos pueden utilizar TCP/ IP para conectar los cajeros automáticos que no se conectan a la red 116 . • Las direcciones 127. • En una red de clase B. Las direcciones privadas pueden ser utilizadas por los equipos que usan traducción de dirección de red (NAT) para conectarse a una red pública o por los hosts que no se conectan a Internet.534 equipos. Hay ciertas direcciones en cada clase de dirección IP que no están asignadas y que se denominan direcciones privadas.214 equipos.255 (16 bits red.168. • La dirección 0.0 a 192. Se denomina dirección de red.x. de modo que la cantidad máxima de equipos es 216 (de nuevo menos dos). En una misma red no pueden existir dos direcciones iguales. y no necesitan conectividad externa. lo que equivale a 65. 16 bits equipos) • Clase C: 192.0. de modo que la cantidad máxima de hosts es 224 menos dos: las direcciones reservadas de broadcast (tres últimos octetos a 255) y de red (tres últimos octetos a 0). reservando el octeto final (8 bits) para que sea asignado a los equipos. Por ejemplo.16.31. • La dirección que tiene a uno todos los bits de su parte de equipo sirve para comunicar con todos los equipos de la red en la que se ubica.255 (8 bits red. o 254 equipos. 8 bits equipos) Muchas aplicaciones requieren conectividad dentro de una sola red. En las redes de gran tamaño a menudo se usa TCP/IP. se asignan los dos primeros octetos para identificar la red.255 (24 bits red.168. Se denomina dirección de bucle local o loopback.x. 22 (de SSH) o 21 (de FTP) entre otros. de modo que en un servidor un usuario sin privilegios no pueda establecer un puerto pasivo en el puerto 80 (dedicado a HTTP). viene formateada de forma que se indica el número. TCP puede hacer uso del puerto 22 e igualmente UDP podría usar el mismo sin provocar colisión. Puertos Los puertos de comunicaciones son la base sobre la que se sustentan los protocolos de transporte TCP y UDP. Estos protocolos establecen conexiones salientes y entrantes en puertos denominados activos o pasivos respectivamente. formateado en dos columnas: el nombre de servicio y el puerto que emplea dicho servicio. además. 3 Esto es debido también a que la mayoría de servicios que se prestan están en este rango. La columna del puerto. Las direcciones privadas también se pueden utilizar en una red en la que no hay suficientes direcciones públicas disponibles.Comunicaciones pública. Según lo acordado. Las direcciones privadas se pueden utilizar junto con un servidor de traducción de direcciones de red (NAT) para suministrar conectividad a todos los equipos de una red que tiene relativamente pocas direcciones públicas disponibles. La asignación de puertos es realizada por los protocolos de transporte. Nota En la mayoría de sistemas operativos existe un fichero de texto plano denominado services que contiene. 117 . cualquier tráfico que posea una dirección destino dentro de uno de los intervalos de direcciones privadas no se enrutará a través de Internet. 25 (de SMTP). Los puertos se representan como números en rango de 16 bits. Los puertos por debajo del 1024 se denominan puertos privilegiados y en sistemas como los UNIX se requieren permisos de super-usuario (o root) para poder emplear estos 3 puertos . de manera que las direcciones privadas son ideales para ellos. una barra inclinada y el tipo de transporte que se emplea: http http ftp-data ftp domain domain 80/tcp 80/udp 20/tcp 21/tcp 53/tcp 53/udp Cada protocolo de transporte puede hacer uso del rango de numeración sin colisionar con ningún otro. 1. cada uno mantiene su propia numeración. que pueden ir desde el 0 hasta el 65535.2. UDP UDP es un protocolo de datagramas. El número del puerto activo puede ser elegido o no dependiendo del protocolo de transporte. El servidor emplearía un puerto pasivo para establecer la comunicación mientras que el cliente usaría uno activo. 2. En el esquema cliente-servidor existe un servidor que se mantiene a la espera de una petición entrante y un cliente de forma activa realizas las peticiones al servidor.Comunicaciones El puerto activo toma de la numeración de puertos un número y lo reserva para establecer la comunicación con el puerto pasivo. Esto hace posible que a un puerto pasivo se pueda conectar más de un puerto activo. No se espera respuesta del mismo. La comunicación se identifica para el sistema operativo con el par de puertos activo/pasivo además de la dirección IP tanto de origen como de destino. Nota En la jerga de los sistemas de comunicación existen algunas palabras clave que se emplean para determinar ciertos aspectos de la comunicación o los elementos que la componen. La palabra anglosajona socket (traducida como zócalo o conector) es la palabra que se suele emplear para indicar una conexión. El tratamiento de este tipo de paquetes carga menos la red y los sistemas operativos pero si la red no es fiable puede existir pérdida de información. Un datagrama puede ser enviado hacia un servidor pero no se comprueba el estado de recepción. Servidor y Cliente UDP Erlang provee un módulo llamado gen_udp que nos permite establecer un puerto pasivo para mantenernos enlazados y recibir paquetes 118 . Como hemos ido comentando a lo largo de la sección hay dos protocolos para transporte que emplean los puertos de comunicación: TCP Es orientado a conexión. Cuando se establece un puerto pasivo mediante TCP se suele decir que el servidor escucha mientras que si es mediante UDP se dice que el servidor está enlazado a ese puerto. Requiere que el cliente formalice la conexión con el servidor y esta se mantiene hasta que una de las dos partes solicita la desconexión o durante un envío se produzca un tiempo de espera agotado. ip_address()} La dirección IP en la que enlazar el puerto. El valor por defecto es true. true | false | once} Indica si todos los mensajes recibidos por red serán pasados al proceso (true) o no (false) o si se hará sólo la primera vez (once). inet | inet6 Emplea IPv4 o IPv6 para las conexiones. No lo bloquea. Por defecto esta opción está deshabilitada. true | false} Permite reutilizar el puerto. permite tanto programar servidores como clientes UDP. El valor por defecto es list. También permite emplear un puerto activo para el envío de un paquete hacia un servidor que se mantenga enlazado. Las opciones son: list | binary Indica si se quiere recibir el paquete como una lista o un binario. Resumiendo. Hay tres funciones que emplearemos con mucha frecuencia en la construcción de servidores y clientes UDP: open/1 o open/2. send/4 y close/1. Opts) -> {ok. 119 . En UDP tanto cliente como servidor deben enlazar un puerto. {active. {ip | ifaddr. La función open/2 se presenta: open(Port. El módulo gen_udp aprovecha las capacidades propias de los procesos enviando cada paquete recibido al proceso que solicitó el enlace con el puerto. Reason} Puede recibir como segundo parámetro opciones que permiten variar la forma en la que establecer el enlace con el puerto. Socket} | {error. El cliente lo hará de forma activa para enviarlos y tener la posibilidad de recibir respuesta del servidor. El servidor lo hará de forma pasiva para recibir mensajes. El valor por defecto es inet.Comunicaciones entrantes. {reuseaddr. El código se inicia mediante la función start/1 indicando un número entero correspondiente al puerto enlazado. port. InPortNo. ok. Vamos a escribir un módulo que enlace el puerto 2020 y cada paquete que reciba lo pase por pantalla: -module(udpsrv).org/doc/man/gen_udp. udp) -> io:format("recibido(~p): ~p~n". -export([start/1. Packet} La definición de estos datos es la siguiente: 120 . #udp{ip=IP. Lanza un nuevo proceso que ejecuta la función init/1 encargada de abrir el puerto y pasar el control a la función loop/1 que se encarga de atender los paquetes que vayan llegando. ip.Comunicaciones Nota Hay disponibles muchas opciones más a las que no entraremos ya que son conceptos más avanzados o muy específicos. Packet when is_record(Packet.msg ]). con lo que salen del ámbito de explicación de este capítulo.ip. loop(Socket) -> receive stop -> gen_udp:close(Socket). IP. [Port]). -record(udp. loop/1]). Socket} = gen_udp:open(Port). Socket. loop(Socket) end. "recibido"). {socket. Si desea más información sobre la función gen_udp:open/2 puede revisar la siguiente dirección: http://www.port=Port} = Packet.erlang. init/1. [ Packet#udp. IP. Port. init(Port) -> {ok.html#open-2 Pondremos en práctica lo aprendido. En el ejemplo hemos usado un registro formado por los cuatro datos que nos envía cada paquete UDP además del identificador: {udp. gen_udp:send(Socket. start(Port) -> spawn(?MODULE. loop(Socket). Packet#udp. msg}). init. {127.... Podemos abrir una consola de Erlang y lanzar el servidor. Packet El paquete recibido. Nos proporciona también otros datos estadísticos como los bytes recibidos (Recv) y enviados (Sent). Socket} = gen_udp:open(0)..1}): "hola mundo!" La operación de conexión se ha realizado abriendo una comunicación con la función gen_udp:open/1 pasando como parámetro el puerto 0. Port [..0> *:2020 [. 2020.. ok recibido({127.0> *:2020 [. InPortNo El puerto origen del paquete recibido..34.Comunicaciones Socket El manejador retornado por la función open/1 u open/2.. ok > inet:i(). {ok..] 11 0 <0.] Recv Sent Owner Local Address [.#Port<0.. Para saber que el puerto se encuentra enlazado emplearemos la función inet:i/0 que nos proporciona información sobre las comunicaciones: > udpsrv:start(2020).0..0. "hola mundo!").34.. El puerto 0 se usa para indicar que queremos que el sistema operativo seleccione un puerto automáticamente por nosotros. Port [.618>} 21> gen_udp:send(Socket... IP La dirección IP en formato de tupla.0.] BOUND DGRAM 121 . Podemos recurrir a ejecutar de nuevo inet:i/0 para ver las conexiones abiertas y las estadísticas: > inet:i().0.] State Type 593 [.] State Type 593 [.] Recv Sent Owner Local Address [.] 0 0 <0.] BOUND DGRAM ok La salida nos muestra el proceso (Owner) que tiene enlazado (State =:= BOUND) el puerto 2020 (Local) de tipo UDP (Type =:= DGRAM).1}. Podemos escribir estas líneas en la consola de Erlang para probar el código del servidor: > {ok... msg}). Packet#udp. En la función gen_udp:open/2 hemos empleado el parámetro de opciones para indicar a gen_udp que no envíe el paquete recibido al proceso.. Por los datos estadísticos vemos que la información que ha enviado el cliente (columna Sent) es la que ha recibido el servidor (columna Recv). Port. ok > {ok. ok. "hola mundo!").{{127. -export([start/1. IP. [{active. Socket} = gen_udp:open(0. {127. false}]). loop(Socket) end.0.38.0> *:33361 [.1}. Podemos probarlo en consola de la siguiente forma: > udpsrv:start(2020). [Port]). Vamos a modificar el código del servidor para que retorne al cliente una cadena de texto: -module(udpsrv).. init(Port) -> {ok.0.msg ]). ip. start(Port) -> spawn(?MODULE.1}.0. port. 1024).1}): "hola mundo!" > gen_udp:recv(Socket.] 0 11 <0. Al 122 .. ok recibido({127.port=Port} = Packet. init/1.0. udp) -> io:format("recibido(~p): ~p~n". {ok. loop(Socket) -> receive stop -> gen_udp:close(Socket).2020. Packet when is_record(Packet. gen_udp:send(Socket."recibido"}} El cliente recibe un paquete del servidor en el que le dice recibido. init. loop(Socket).] BOUND DGRAM Ahora vemos dos líneas.0. "recibido").0. {socket.ip. loop/1]).599>} > gen_udp:send(Socket. -record(udp.Comunicaciones 618 ok [. La primera sigue siendo la del servidor y la segunda pertenece al cliente. {ok. [ Packet#udp.. Hemos empleado la función gen_udp:send/4 para enviar al remitente una respuesta enviando un texto fijo al cliente. #udp{ip=IP.#Port<0. Socket} = gen_udp:open(Port). 2020. El valor por defecto es true. Por defecto esta opción está deshabilitada. 3. Options) -> {ok. El valor por defecto es list. Reason} Las opciones disponibles son iguales a las que se mostraron en la función gen_udp:open. true | false} Permite reutilizar el puerto. ip_address()} La dirección IP en la que enlazar el puerto. {ip | ifaddr. Si desea más información sobre la función gen_tcp:listen/2 puede revisar 4 este enlace 4 http://www. El valor por defecto es inet.erlang. {active. con lo que salen del ámbito de explicación de este capítulo. {reuseaddr. inet | inet6 Emplea IPv4 o IPv6 para las conexiones. Nota Hay disponibles muchas opciones más a las que no entraremos ya que son conceptos más avanzados o muy específicos. true | false | once} Indica si todos los mensajes recibidos por red serán pasados al proceso (true) o no (false) o sólo la primera vez (once). ListenSocket} | {error.html#listen-2 123 . Son las siguientes: list | binary Indica si se quiere recibir el paquete como una lista o un binario. Servidor y Cliente TCP Para establecer comunicaciones TCP en Erlang disponemos del módulo gen_tcp. No lo bloquea. En TCP el servidor escucha de un puerto y se mantiene aceptando peticiones entrantes que quedan conectadas tras su aceptación.org/doc/man/gen_tcp. La dinámica está protocolizada de forma que un servidor establece una escucha en un puerto específico con posibilidad de envío de opciones a través de la función listen/2 cuya definición es: listen(Port.Comunicaciones ejecutar gen_udp:recv/2 es cuando se obtiene la información que llega del servidor. 2020. {ok.] Local Foreign State Type 599 [. En otra consola podemos realizar la conexión entrante... Socket} | {error.599>} 2> inet:i().] State Type 609 [. {ok. true}]). Port.. el puerto al que conectarse (Port) y las opciones para establecer la comunicación (Options). La función emplea la variable Socket creada en listen/2 para esperar nuevas conexiones entrantes.. Este socket se empleará para comunicarse con el cliente y obtener información del mismo. []). Desde la nueva consola podemos enviar información al servidor a través de la función send/2 la cual tiene la forma: 124 .0.0.1}.] LISTEN STREAM ok El siguiente paso será mantener el proceso a la espera de una conexión entrante.0> *:2020 [. Es el proceso que se llama aceptación y se realiza con la función accept/1. Port [..] 0 0 <0.#Port<0.. Port [.] Recv Sent Owner Local Address [.32. Socket} = gen_tcp:connect({127. Reason} Como parámetros se indica la dirección IP a la que conectarse (Address en formato tupla)....609>} 3> inet:i(). Emplearemos la función connect/3 que tiene la siguiente sintaxis: connect(Address...] *:38139 *:2020 CONNECTED STREAM ok La nueva consola de Erlang sólo tiene constancia de una conexión existente que está en estado conectada (State) y va del puerto local 38139 al puerto 2020..Comunicaciones Si ponemos en escucha el puerto 2020 y volvemos a listar el estado de la red podemos ver que el proceso (en Owner) pasa a escuhar (State =:= LISTEN) en el puerto 2020 (en Local) y para el tipo TCP (Type =:= STREAM): > {ok.. Socket} = gen_tcp:listen(2020.. Abrimos otra consola y establecemos una comunicación local entre ambos puntos escribiendo lo siguiente: > {ok. Si lo ejecutamos en la consola de Erlang el sistema se quedará bloqueado a la espera de una conexión entrante: > {ok.] Recv Sent [.] 0 0 [. Las opciones son las mismas que se describieron para la función listen/2. Options) -> {ok. Cuando una conexión entrante llega la función accept/1 acepta la conexión y finaliza su ejecución retornando otra variable SockAceptado.#Port<0... SockAceptado} = gen_tcp:accept(Socket). [{reuseaddr. Si ejecutamos 5 flush/0 aparecerán los mensajes recibidos al proceso de la consola . 4. El Packet es la información que queremos enviar. false} el mensaje no es enviado al proceso sino que espera a que lo recibamos a través del uso de la función recv/2. En caso de que el paquete sea mayor que el tamaño una nueva ejecución de recv/2 recogerá el siguiente trozo de información. Esta función tiene la siguiente sintaxis: recv(Socket. Para establecer el diálogo entre servidor y cliente sólo necesitamos emplear las funciones send/2 y recv/2 para realizar la comunicación bidireccional. Conviene cerrar los puertos antes de finalizar la ejecución de los servidores para liberar los puertos empleados. Packet) -> ok | {error. Véase apéndice B para más información. Reason} Como Socket emplearemos el que nos retornó la función connect/2. 125 .Comunicaciones send(Socket. Length es el tamaño máximo que se espera recibir del paquete. Packet} | {error. El paquete permite formatos de lista de caracteres y lista binaria. En caso de indicar un tamaño cero se recibe todo el paquete sin limitación de tamaño. Length) -> {ok. En Erlang se conecta cada socket a un proceso para recibir información por 5 Recordemos que la consola es un proceso que se mantiene a la espera de recibir eventos. Todos los eventos que reciba la consola son interceptados por esta. En el momento en el que se desee finalizar la comunicación por cualquiera de las dos partes emplearíamos la función close/1. "hola mundo!"). En el servidor no vemos de momento nada por consola. Servidor TCP Concurrente La comunicación TCP se basa en la interconexión de dos puntos. Importante El socket que se estableció para escucha se puede igualmente cerrar con close/1. Reason} El Socket es el valor de retorno obtenido tras la ejecución de la función accept/1. Podemos ejecutarlo de la siguiente manera: > gen_tcp:send(Socket. Si agregamos a las opciones de la función listen/2 {active. [Msg]). gen_tcp:send(Socket. Pid). srv_loop(Socket). que tiene la forma: controlling_process(Socket. Opts). {ok. true}]). Para que el proceso de servidor no permanezca bloqueado atendiendo la conexión del primer cliente que conecte generamos un nuevo proceso para atender esa petición entrante. true}. La función start/1 se encarga de lanzar en un proceso aparte la ejecución de la función srv_init/1. Reason} Escribiremos un pequeño módulo para comprobar cómo funciona. Socket} -> io:format("Finalizado. Pid) -> ok | {error. Socket. Socket} = gen_tcp:listen(Port.~n"). Any -> io:format("Mensaje no reconocido: ~p~n". En TCP cada conexión servidora genera un socket de conexión con el cliente específico. Salida). timer:sleep(5000). Msg]). Pid = spawn(fun() -> worker_loop(SockCli) end). [{active. false}]. srv_loop(Socket) -> {ok. {active. Este problema no se presenta en comunicaciones UDP.Comunicaciones lo que hasta que la conexión entre cliente y servidor no se cierra ningún otro cliente puede ser atendido por el servidor. Msg} -> io:format("Recibido ~p: ~p~n". [self(). SockCli} = gen_tcp:accept(Socket). [Any]) end. %% 5 segundos de espera Salida = io_lib:format("Eco: ~s". inet:setopts(SockCli. worker_loop(Socket). srv_init(Port) -> Opts = [{reuseaddr. worker_loop(Socket) -> receive {tcp. El bucle de ejecución para el servidor se basa en aceptar una conexión. gen_tcp:controlling_process(SockCli. srv_loop(Socket). De esta forma el proceso principal queda liberado para aceptar más peticiones y generar nuevos procesos a medida que vayan llegando nuevas peticiones de clientes. El cometido de esta función inicializadora es establecer la escucha en un puerto TCP. Llamaremos al módulo tcpsrv y agregaremos las funciones del servidor: start(Port) -> spawn(fun() -> srv_init(Port) end). {tcp_closed. generar un 126 . Para que el nuevo socket generado sepa que tiene que enviar sus paquetes al nuevo proceso hay que emplear la función controlling_process/2. {ok. gen_tcp:send(Socket. El siguiente código es un ejemplo para probar el servidor: cli_send(Port. MsgSrv} -> io:format("Retornado ~p: ~p~n". cli_send. Socket. receive {tcp. enviar un mensaje y esperar por el retorno antes de finalizar la comunicación. lists:foreach(Send. MsgSrv]). 6 Este código genera 10 procesos que ejecutan la función cli_send/2 enviando el mensaje i=I. [Any]) end. Hasta el momento todo funciona como la versión anterior. [I]).1. 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.0. La función cli_send/2 permite conectarse a un puerto local .Comunicaciones nuevo proceso. [self(). En nuestro ejemplo esperamos a recibir un mensaje y lo retornamos precedido de la palabra Eco. No obstante. Socket} = gen_tcp:connect({127. Ventajas de inet Erlang no sólo dispone de funciones para manejar las comunicaciones a nivel transporte. por lo que se emplea por defecto la IP local o 127. La ejecución muestra como todos los procesos llegan a recibir el mensaje en el servidor y quedan esperando por el resultado 5 segundos después. Msg).10)). Opts). 5. 127 .1}.0. Any -> io:format("Mensaje no reconocido: ~p~n". Esta función integraría el protocolo a nivel de aplicación para interactuar con el cliente. Port. true}]. spawn(tcpcli. El módulo inet a través de la función setopts/2 provee 6 En este ejemplo no hemos empleado direcciones IP. siendo I el valor pasado por foreach/2 a cada uno de los procesos.0. Msg) -> Opts = [{active. lists:seq(1. Text]) end. Lo podemos realizar con varias consolas o a través de un código como el siguiente: cli_concurrent_send(Port) -> Send = fun(I) -> Text = io_lib:format("i=~p".0. La generación del nuevo proceso tiene como función de bucle principal a worker_loop. gen_tcp:close(Socket). pasarle el control de la conexión con el cliente al nuevo proceso y vuelta a empezar. [Port. 'GET'. Opts).#Port<0. http}].#Port<0.Comunicaciones la capacidad de interpretar los paquetes recibidos a través de TCP o UDP y enviarlos como mensaje al proceso ya procesados. SunRPC.0 200 OK > Content-length: 1 > Content-type: text/plain > > H". {ok. {http_header. Shell got {http. ok Los mensajes recibidos por el sistema son tuplas que tienen como primer elemento http. SC} = gen_tcp:accept(Socket). el envío deberemos de componerlo nosotros mismos y enviarlo con la función de send/2 de gen_tcp."localhost:8080"}} [. {packet.#Port<0. Msg). Para construir nuestro propio servidor HTTP y aprovechar la característica que nos provee inet sólo tendríamos que agregar la opción a la función listen/2. true}.14. Como tercer parámetro puede aparecer otra tupla cuyo primer parámetro es: 7 http://www.erlang.{1. TPKT y HTTP.{abs_path. > {ok.605>} > flush().605>. Según la documentación de inet los formatos que procesa son: CORBA. {active.'Host'.undefined. En este momento el sistema queda en espera de que llegue una petición."/"}.org/doc/man/inet.] Shell got {http. FastCGI. ASN-1. Vamos a verlo con un ejemplo: > Opts = [{reuseaddr.html#setopts-2 128 ..605>.604>} > {ok. vamos a abrir un navegador con la siguiente URL: http://localhost:8080/ En la consola veremos que ya prosigue la ejecución: {ok. {http_request. Socket} = gen_tcp:listen(8080.#Port<0. Como hemos levantado un puerto TCP y le hemos configurado las características de HTTP. Como en los casos de tcp el segundo parámetro es Socket.1}}} Shell got {http. > gen_tcp:send(SC. 7 Nota La decodificación la realiza únicamente a nivel de recepción.#Port<0.http_eoh} ok > Msg = "HTTP/1. Line.605>. true}.. srv_loop(Socket). gen_tcp:controlling_process(SockCli. srv_init(Port) -> Opts = [{reuseaddr. 'GET'. Indica que la recepción de cabeceras ha finalizado. raw}]). valor reservado (undefined) y valor de la cabecera. nombre de la cabecera. -define(RESP."/"}. {http. {active. worker_loop(Socket). Header} -> io:format("Recibido ~p: ~p~n". true}.Comunicaciones http_request Si se trata de la primera línea de petición. inet:setopts(SockCli. http}]. Socket. Socket. Pid). http_eoh} -> inet:setopts(Socket. Que se estructuran en una tupla de 5 campos: http_header.1}} http_header Las siguientes líneas a la petición son las líneas de cabecera. método HTTP (GET. srv_loop(Socket) -> {ok. Pid = spawn(fun() -> worker_loop(SockCli) end). false}. http_eoh Este dato se transmite en forma de átomo. PUT o DELETE entre otros). POST. [self(). Esta tupla tendrá 4 campos: http_request. Un ejemplo: {http_request. [{active. bit de cabecera. Presenta las peticiones recibidas por pantalla junto con su contenido: -module(httpsrv). {packet. -export([start/1]). {tcp. Socket} = gen_tcp:listen(Port. worker_loop(Socket) -> receive {http. true}]). {ok. Opts). {abs_path. srv_loop(Socket). Msg} -> 129 . A continuación vemos un ejemplo completo. SockCli} = gen_tcp:accept(Socket). worker_loop(Socket). Header]).1 200 OK Content-Length: 2 Content-Type: text/plain OK"). start(Port) -> spawn(fun() -> srv_init(Port) end).{1. "HTTP/1. URI y versión HTTP en forma de tupla de dos elementos. Socket. [{packet. {tcp_closed. Any -> io:format("Mensaje no reconocido: ~p~n". Además vemos que se diferencian bien los mensajes que se reciben de tipo http de los que son de tipo tcp. Msg]). gen_tcp:close(Socket). [Any]). 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. 130 . ?RESP).~n"). [self().Comunicaciones io:format("Recibido ~p: ~p~n". HttpPacket}. HttpPacket}. mientras que el retorno de receive será: {http. El servidor es bastante simple ya que siempre retorna el mismo resultado. Si accedemos desde un navegador veremos en modo texto el mensaje OK. gen_tcp:send(Socket. Nota Si empleamos el parámetro {active. Socket. false} para emplear la función recv/2 en lugar de receive hay que tener presente que el retorno de la función recv/2 será: {ok. Socket} -> io:format("Finalizado. gen_tcp:close(Socket) end. gen_tcp:close(Socket). los directorios que es conveniente crear y la disposición de los ficheros dentro de estos directorios.erl.Capítulo 8. Aún no hemos comentado la forma que debe tener nuestro espacio de trabajo. 1 Basho Technologies es una empresa estadounidense que desarrolla la base de datos Riak. Iniciar un Proyecto A lo largo de los capítulos hemos realizado la mayor parte del código en la consola de Erlang y vimos la organización del código interno y la realización de módulos. Un proyecto en Erlang/OTP debe disponer de una estructura base como la siguiente: src Este directorio contendrá el código fuente. 1. 131 . Ecosistema Erlang La 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.beam. 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 de Erlang. La herramienta rebar es la más empleada entre las utilidades de terceros 1 del mundo Erlang. La empresa Basho es la desarrolladora principal de esta herramienta aunque cada día hay más contribuidores al proyecto. En software se ha tomado esta definición para definir al conjunto de herramientas y sistemas que permiten realizar software. Todos los ficheros cuya extensión sea . ebin Aquí se almacenarán los ficheros de tipo . es decir la compilación de nuestra aplicación. En Erlang lo usaremos para identificar el uso de unas herramientas junto con sus buenas prácticas a la hora de desarrollar proyectos de software. —Charles Babbage Un ecosistema es un ambiente en el que conviven elementos en un espacio relacionandose entre sí. El sistema generará el script rebar en ese mismo directorio. hojas de estilo CSS o códigos JavaScript entre otros. MacOS X u OpenSolaris entre otros. Linux. test para los códigos de pruebas de EUnit o CommonTest o deps es donde se bajan otros proyectos de terceros para incluir su código dentro de nuestro proyecto. $ git clone git://github.hrl.Ecosistema Erlang include Los ficheros que se almacenan en este directorio son los de tipo cabecera . Para esta tarea nos ayudaremos de rebar. Recompile: src/rebar_utils ==> rebar (compile) Congratulations! You now have a self-contained script called "rebar" in your current working directory. Creamos los tres directorios base y pasamos a instalar rebar.. priv Cuando el proyecto requiere de ficheros específicos para funcionar se introducen en este directorio ficheros como certificados.git $ cd rebar $ . 132 . Instalar rebar La instalación de rebar se basa en descargar el código de su repositorio y ejecutar el script bootstrap. Ahora solo nos falta copiar el script generado a una ruta visible por nuestro PATH.com/basho/rebar.. /usr/local/bin o /opt/local/ bin. 2 Sistemas Unix o tipo Unix como BSD. Normalmente como super usuario en sistemas de tipo 2 Unix en una ruta como /usr/bin. Place this script anywhere in your path and you can use rebar to build OTPcompliant apps. páginas HTML. 1. Este script se puede copiar a un directorio del PATH o localmente dentro del proyecto que estemos desarrollando.1. Crearemos un proyecto que ilustre cómo organizar los ficheros del código y cómo ejecutar ese mismo código de forma autónoma. Nota Hay más directorios por defecto para proyectos Erlang/OTP como c_src donde se alojan los ficheros de extensión escritos en C./bootstrap Recompile: src/getopt . {http_header. State ++ [{Key. 3 https://github. srv_init(Port) -> Opts = [{reuseaddr. Pid = spawn(fun() -> worker_loop(SockCli. Socket} = gen_tcp:listen(Port. Response). Value}]). -export([start/1]). Opts). {path. true}]). {http. http_eoh} -> Response = fileserver:send(State). srv_loop(Socket). true}. Path} = TPath. error_logger:info_msg("Peticion: ~p~n". 1. Method}. worker_loop(Socket. start(Port) -> spawn(fun() -> srv_init(Port) end). Pid). Recomiendo que los proyectos iniciales y el aprendizaje se lleven a cabo en sistemas tipo Unix como MacOS o GNU/Linux por el motivo de que la mayoría de soporte y desarrollos se realizan en estos sistemas. Socket. worker_loop(Socket. _.Ecosistema Erlang Para asegurarnos de que es accesible podemos ejecutarlo en la consola. []) end). [{active. Escribiendo el Código Vamos a crear un proyecto que consistirá en un servidor web al que se le solicitarán ficheros y en caso de existir en el directorio priv se retornará su contenido. State) -> receive {http. Nota La utilidad rebar se encuentra también disponible para Windows 3 a través del repositorio bifurcado (fork) de IRONkyle . _}} -> {abs_path. Socket. Value}} -> worker_loop(Socket. State ++ [ {method. El primer módulo se encargará de establecer la escucha para el servidor web y atender las peticiones: -module(webserver). Method.2.com/IRONkyle/rebar 133 . TPath. gen_tcp:send(Socket. _. srv_loop(Socket). gen_tcp:close(Socket). {http_request. {packet. Key. http}]. inet:setopts(SockCli. srv_loop(Socket) -> {ok. Path} ]). {ok. {active. Socket. El desarrollo lo realizaremos en dos ficheros. {http. SockCli} = gen_tcp:accept(Socket). [Path]). false}. gen_tcp:controlling_process(SockCli. 134 . [byte_size(Content)]) ). Request. "/"). RealPath = filename:join(CWD. <<"HTTP/1.Ecosistema Erlang {tcp_closed.~n"). ".png" -> <<"image/png">>. send(Request) -> "/" ++ Path = proplists:get_value(path.js" -> <<"application/javascript">>. _} -> ?RESP_404 end.css" -> <<"text/css">>. ". Path). "\r\n\r\n". Socket} -> error_logger:info_msg("Finalizado. ". mimetype(File) -> case filename:extension(string:to_lower(File)) of ". El código es el siguiente: -module(fileserver). Type = mimetype(Path). -define(RESP_200. {error. Type/binary.xml" -> <<"application/xml">>. [Any]). -export([send/1]).microsoft. Solo agregaremos la llamada al módulo fileserver. ".jpg" -> <<"image/jpeg">>. Content} -> Size = list_to_binary( io_lib:format("~p". ".icon">>.1 200 OK Server: Erlang Web Server Connection: Close Content-type: ">>).zip" -> <<"application/zip">>. -define(RESP_404.html" -> <<"text/html">>. “Servidor TCP Concurrente” del Capítulo 7.1 404 Not Found Server: Erlang Web Server Connection: Close ">>). ". {ok. ".jpeg" -> <<"image/jpeg">>. El código listado fue visto en la Sección  4. <<"HTTP/1.ico" -> <<"image/vnd. ". El segundo módulo se encargará de buscar el fichero solicitado y retornarlo como texto identificando su tipo. ". CWD} = file:get_cwd(). Content/binary >>. Comunicaciones. << ?RESP_200/binary. case file:read_file(RealPath) of {ok. "\nContent-lenght: ". gen_tcp:close(Socket). gen_tcp:close(Socket) end. Any -> error_logger:info_msg("No reconocido: ~p~n". Size/binary.htm" -> <<"text/html">>. Ecosistema Erlang _ -> <<"text/plain">> end. Con esto ya tenemos el código preparado. Solos nos falta escribir la definición necesaria para que rebar pueda identificar la aplicación y construir el producto final. Crearemos el fichero en el directorio src con el nombre webserver.app.src: {application, webserver {description {vsn , "1.0"}, , [ , "Erlang Web Server"}, {applications ,[ kernel, stdlib, inets ]} ]}. El nombre que tendrá la aplicación. Como descripción se especifica un texto. Es deseable que no sea muy extenso. Se puede poner el nombre completo de la aplicación. La versión de la aplicación. Se puede especificar de la forma que se 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 palabras clave: git, hg, bzr, svn o {cmd, Cmd}. Las primeras indican al sistema que tome el tag o el número de revisión del sistema de control de versiones. La última indica que ejecute el comando contenido en Cmd para obtener la versión. En la página de referencia de app podemos ver una lista más completa y detallada de las opciones que permite el fichero para iniciar una aplicación. 4 2. Compilar y Limpiar Una vez que tenemos el directorio src creado podemos compilarlo todo ejecutando el comando: rebar compile. El comando rebar se encarga de crear el directorio ebin y depositar los ficheros beam dentro de él: $ rebar compile ==> webserver_simple (compile) Compiled src/webserver.erl Compiled src/fileserver.erl 4 http://www.erlang.org/doc/man/app.html 135 Ecosistema Erlang El espacio de trabajo es como se puede observar a continuación: El directorio ebin contiene la compilación de los códigos listados en la sección anterior. El fichero webserver.app.src se analiza y se completa para generar el fichero webserver.app dentro del directorio ebin. Si queremos ejecutar el código, podemos iniciar la consola de la siguiente forma: $ erl -sname webserver -pa ebin (webserver@bosqueviejo)1> webserver:start(8888). <0.39.0> De esta forma tenemos el código en ejecución. Para eliminar estos ficheros generados ejecutamos el comando rebar clean. 3. Creando y lanzando una aplicación En la sección anterior vimos que el lanzamiento del código escrito se hacía de forma manual. Si desarrollamos una aplicación de servidor hay que poder lanzar esta aplicación de forma automática. Erlang proporciona al programador una forma de realizar esto a través de 5 un comportamiento denominado application. Para ello necesitamos crear otro fichero de código dentro del directorio src. Llamaremos a este fichero webserver_app.erl y pondremos el siguiente contenido: -module(webserver_app). 5 Los comportamientos (behaviours) son un mecanismo de inversión de control (IoC) que posibilita la creación de código abstracto más concreto para el usuario. Estos serán vistos en mayor profundidad en el Volumen II. 136 Ecosistema Erlang -behaviour(application). -export([start/0, start/2, stop/1]). start() -> application:start(webserver). start(_StartType, _StartArgs) -> {ok, webserver:start(8888)}. stop(_State) -> ok. Este módulo dispone de tres funciones. Las funciones start/2 y stop/1 son requeridas por el comportamiento application, mientras que start/0 la emplearemos para la línea de comandos. En el fichero webserver.app.src solo debemos de agregar una nueva línea que indique qué módulo se hará cargo de las llamadas propias de la 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 se hará cargo del inicio y parada de la aplicación. En la consola agregaremos un par de argumentos más : $ erl -sname test -pa ebin -s inets -s webserver_app \ -noshell -detached 6 El comando se encarga de dar un nombre al nodo (-sname), decir donde se encuentra el código que queremos lanzar (-pa), arrancar la aplicación inets (-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 segundo plano (-detached). 4. Dependencias En Internet existen repositorios con miles de librerías para Erlang. 7 8 Los más representativos son github.com y bitbucket . En estos sitios 6 Los argumentos usados para la línea de comandos se pueden revisar en el Apéndice  B, La línea de comandos. 7 https://github.com 8 https://bitbucket.org 137 Para ello generaríamos el fichero de configuración rebar. webserver.[ kernel. o frameworks web como ChicagoBoss o Nitrogen. o frameworks para crear servidores web como cowboy o mochiweb entre otras muchas. inets. La función start/0 quedaría así: -module(webserver_app). {vsn.git 138 . stop/1]). {applications. 9 Podemos emplear en su lugar la librería mimetypes . start(_StartType. "https://github. []}} ]}. 9 https://github. La herramienta rebar posibilita a través de su fichero de configuración que podamos instalar en nuestro proyecto una librería externa con muy poco esfuerzo. El código del fichero fileserver. PostgreSQL. Agregamos en el fichero webserver_app. webserver:start(8888)}. -export([start/0. stdlib. start/2. mimetypes ]}.config en la ruta raíz del proyecto y con el siguiente contenido: {deps. Memcached. -behaviour(application).erl el lanzamiento de la aplicación mimetypes para cumplir con la dependencia. {webserver_app. application:start(webserver). "Erlang Web Server"}.com/spawngrid/mimetypes. _StartArgs) -> {ok. [ {description.0"}.com/spawngrid/mimetypes. {mod.git". "1. start() -> application:start(mimetypes). {git. [ {mimetypes. "master"} } ]}.Ecosistema Erlang podemos encontrar librerías para conectar con MySQL.*". ". Esa función es insuficiente para cubrir todos los tipos posibles de ficheros que pudiésemos utilizar en nuestra aplicación. La especificación de la aplicación la cambiamos también para agregar la nueva dependencia de la siguiente manera: {application.erl muestra una función llamada mimetype/1. Por último. ==> mimetypes (get-deps) $ rebar compile ==> mimetypes (compile) Compiled src/mimetypes_scan.1 200 OK Server: Erlang Web Server Connection: Close Content-type: ">>). -define(RESP_404. -define(RESP_200. Path). <<"HTTP/1.erl Compiled src/mimetypes_sup. << ?RESP_200/binary. Request. RealPath = filename:join(CWD.. "https://github. {error.erl Compiled src/mimetypes_app. Type/binary. -export([send/1]).xrl Compiled src/mimetypes_parse.erl 139 . CWD} = file:get_cwd(). "master"} Cloning into 'mimetypes'. send(Request) -> "/" ++ Path = proplists:get_value(path. Content/binary >>.com/spawngrid/mimetypes. Size/binary. "/").Ecosistema Erlang stop(_State) -> ok. Content} -> Size = list_to_binary( io_lib:format("~p".erl Compiled src/mimetypes_scan.. Antes de compilar debemos de lanzar el siguiente comando para descargar las dependencias que hemos indicado que necesitamos en nuestro proyecto: $ rebar get-deps ==> webserver_deps (get-deps) Pulling mimetypes from {git. cambiamos el código escrito para que en lugar de tener el uso de nuestra función mimetype/1 emplee las que provee la librería: -module(fileserver). case file:read_file(RealPath) of {ok. {ok. <<"HTTP/1. "\r\n\r\n". "\nContent-lenght: ". _} -> ?RESP_404 end. [byte_size(Content)]) ).yrl Compiled src/mimetypes_loader. [Type] = mimetypes:filename(Path).git".1 404 Not Found Server: Erlang Web Server Connection: Close ">>). config HTTP/1.0. Este debe de poderse empaquetar.erl ==> webserver_deps (compile) Compiled src/webserver_app. Liberar y Desplegar El desarrollo de software tiene su culminación cuando el software puede ser instalado en sistemas en producción.com/spawngrid/mimetypes.erl Compiled src/mimetypes_parse. 5.git". El despliegue consiste en el procedimiento de instalación de este código liberado.0. ". "master"} } ]}.erl Compiled src/fileserver. 140 .1 200 OK Server: Erlang Web Server Connection: Close Content-type: application/octet-stream Content-lenght: 119 {deps. Liberar el código consiste en dejar preparado el producto para su instalación.erl Compiled src/webserver. {git.0. construir y lanzar de forma fácil y simple.0.*".0:* LISTEN $ curl -i http://localhost:8888/rebar.0:8888 0. Este último comando es útil para realizar una limpieza del proyecto junto con rebar clean: $ rebar del-deps ==> mimetypes (delete-deps) ==> webserver (delete-deps) $ rebar clean ==> webserver (clean) Para lanzar de nuevo la aplicación agregaremos la ruta de las dependencias de esta forma: $ erl -pa deps/*/ebin -pa ebin -sname test -s inets \ -s webserver_app -noshell -detached Vemos al ejecutarlo que volvemos a tener el puerto 8888 disponible y los ficheros solicitados presentan ya unos tipos MIME más precisos: $ netstat -tln | grep 8888 tcp 0 0 0.erl Nota El comando rebar get-deps se emplea para descargar las dependencias mientras que rebar del-deps se encarga de eliminarlas.Ecosistema Erlang Compiled src/mimetypes. "https://github. [ {mimetypes. config para adaptarlos a la nueva ubicación del código. Dentro de webserver moveremos los directorios src y ebin.Ecosistema Erlang La herramienta rebar nos facilita la tarea de la liberación generando una serie de scripts y ficheros de configuración. Crearemos el directorio rel y dentro de él ejecutaremos el comando: $ rebar create-node nodeid=webserver Importante El nombre que se dé al nodo es preferible que no contenga guiones bajos. En el proceso de generación de actualizaciones (appups y upgrades) podría generar errores.config basta con agregar esta línea: 141 . Ajustaremos los ficheros rebar. Lo siguiente será crear el directorio apps y un subdirectorio webserver.config y el nuevo fichero dentro del directorio rel llamado reltool. En el fichero rebar. Para profundizar más el tema puedes visitar su página de documentación 10 oficial . App. [App1. [Dir1. Nota El directorio apps se emplea cuando se requieren escribir programas con varias aplicaciones. stdlib. En este apartado recogeremos las más importantes que se agregarán bajo la clave sys: {lib_dirs.App2. sasl. la versión de la aplicación (segundo parámetro) y las aplicaciones a ejecutar. webserver ]}.. development | standalone | embedded} El perfil indica el nivel de restricción a aplicar para la copia de dependencias. Las posibles entradas de configuración que se pueden agregar al fichero son muy numerosas. Vsn. [".. {rel.config es la configuración que necesita el sistema denominado reltool para generar la liberación. "webserver". standalone y embedded. {boot_rel./deps"]}.Ecosistema Erlang {sub_dirs. "1. El fichero reltool./apps"..AppN]} Se especifica el nombre de la aplicación (primer parámetro). App} Se pueden crear tantos apartados rel como se necesiten. mimetypes.html 142 ..0".erlang. [ kernel.org/doc/man/reltool. Por lo tanto deberemos de agregar: {rel. ". 10 http://www.DirN]} El directorio (o directorios) que contiene las aplicaciones. ["apps/*"]}.Dir2. inets. El comando rebar generate requiere que esta estructura exista para realizar la liberación. Deberemos de agregarlo de la siguiente forma: {lib_dirs. El listado de aplicaciones debe contener las applicaciones en el orden en el que se deben de ir iniciando cuando se arranque el programa. Uno de ellos debe marcarse por defecto con esta opción. Hay tres perfiles de menos a más restrictivo: development. librerías o binarios entre otros. {profile. [ConfList]} Esta es la especificación individual de cada aplicación.Ecosistema Erlang {incl_cond.app y 11 derivados . app Se incluyen todos los módulos listados en el fichero . {app. include | exclude | derived } Indica el modo en que serán elegidas las aplicaciones que entrarán en la liberación. Como configuración se puede indicar 11 Se refiere a todos los módulos que no estén incluídos pero que reltool detecte que puedan ser utilizados. derived Se incluyen los módulos que estén siendo usados por los incluídos explícitamente. Debe de existir al menos la principal. exclude Entran solo las aplicaciones que se indiquen de forma explícita que deban de entrar. {mod_cond. 143 . derived Se incluyen las aplicaciones indicadas explícitamente y todas sus dependencias. none No se incluye ninguno. App. Las opciones que permite son: all Se incluyen todos los módulos de cada aplicación incluída en la liberación. ebin Se incluyen todos los módulos que estén en el directorio ebin 11 de la aplicación y los derivados . all | app | ebin | derived | none} Es como incl_cond pero a nivel de aplicación. Las opciones disponibles son: include Entran todas las aplicaciones menos la que explícitamente se indique que no entre. ["\. Estos comandos adicionales son del tipo crear directorio (mkdir). include}]} ]}.gitignore"]}. inets. "start_clean".. "webserver"}. {excl_app_filters.. No profundizaremos en este tema para no extendernos más y porque este uso es más una referencia que el lector puede encontrar fácilmente en la web oficial. [{mod_cond. [". [ kernel. "webserver". Aquí el fichero completo reltool. [{mod_cond. derived}./apps". [ kernel. 144 . webserver. "". [{mod_cond. "1. {incl_cond. "^erts. {app_file.Ecosistema Erlang una entrada incl_cond que aplique solo sobre la aplicación y opcionalmente otra mod_cond que actúe solo sobre la aplicación. mimetypes. webserver.config: {sys. derived}.*"]}.0". [ "^bin/.*/(doc|info|include|lib|man|src)"] }. {excl_archive_filters. {incl_cond. sasl./deps"]}. "^erts.*/bin/(dialyzer|typer)". {mod_cond. {rel. [ {lib_dirs. stdlib. {boot_rel. strip}]}. embedded}. Otras entradas al mismo nivel de sys que se pueden encontrar son target_dir que indica el nombre del directorio donde se situará el resultado de la liberación y overlay que contiene comandos adicionales a ejecutar durante la liberación. app}. En nuestro fichero pondremos únicamente: {app. %% Do not archive built libs {excl_sys_filters. strip}. sistema o aplicación). {incl_cond. stdlib ]}. {profile. ". [". {app_file. copiar fichero (copy) o emplear una plantilla (template) a fusionar con un fichero de variables y generar el fichero que estará disponible en el despliegue. derived}. {app. {rel.*". {erts. app}. webserver ]}. include}]} Nota Se pueden emplear filtros para agregar ciertos ficheros sólo y según qué nivel (archivo. attach Permite conectarse a una aplicación ejecutándose en segundo plano. ping Hace un ping al nodo de la aplicación. "\{\{erts_vsn\}\}/bin/erl"}. "bin/webserver. Si ejecutamos el comando start y después ping podremos ver que el sistema responde sin problemas.args".config".cmd". {copy. Entramos a consola a través de attach y podemos ver los mensajes de log que hayamos escrito en el fichero: $ webserver/bin/webserver start 145 . {copy. "files/webserver. "bin/install_upgrade. "files/start_erl.Ecosistema Erlang {target_dir. {copy.escript". El comando rebar nos proporciona en el directorio bin un script que nos permite lanzar el programa de varias formas: console En primer plano. "releases/\{\{rel_vsn\}\}/sys. {overlay. "bin/webserver"}. {copy. En caso de que esté activo el nodo responderá con un pong. {copy. Dentro del directorio rel ejecutamos el comando: $ rebar generate ==> rel (generate) Obtenemos como resultado un directorio webserver en el que se encuentra la liberación. "files/sys.cmd"}.escript"}. start / stop Estos comandos permiten iniciar y detener la aplicación que se lanza en segundo plano. "releases/\{\{rel_vsn\}\}/vm. {copy. [ {mkdir. "log/sasl"}. "files/nodetool". "files/erl". "files/webserver". "webserver"}. "bin/start_erl.args"} ]}. {copy.cmd"}. "files/install_upgrade. {copy. Abre una consola y ejecuta todas las aplicaciones mientras vemos en pantalla los mensajes que imprime cada una de las aplicaciones al lanzarse.config"}.cmd". "\{\{erts_vsn\}\}/bin/nodetool"}. "files/vm. 0. En la Sección  8.1 404 Not Found 146 . El despliegue en un servidor u otro equipo informático se realizará comprimiendo el resultado que se ha obtenido en webserver y descomprimirlo en el destino. -export([send/1]). Lo primero que haremos será generar la versión 1.Ecosistema Erlang /tmp/rel$ webserver/bin/webserver ping pong /tmp/rel$ webserver/bin/webserver attach Attaching to /tmp/rel/webserver/erlang. “Recarga de código” del Capítulo  5. <<"HTTP/1. En esta sección veremos cómo realiza esta acción rebar para cambiar el código en caliente de todo un proyecto completo. hagamos una compilación en un fichero comprimido de la misma.0 y modificar el nombre dentro del directorio rel de webserver a webserver_old. El lanzamiento lo podemos agregar como script en los sistemas tipo Unix gracias a que respeta la forma de start y stop. Como ejemplo pensemos que necesitamos modificar el fichero fileserver. Actualizando en Caliente Una de las ventajas que reseñamos de Erlang al principio es su capacidad para cambiar el código en caliente sin necesidad de detener la ejecución del programa.1)1> Importante Cuando nos conectamos a una aplicación en ejecución con attach debemos siempre salir con la pulsación de las teclas Control+D.pipe. Esto nos servirá para poder transportar nuestro proyecto a producción y crear actualizaciones. -define(RESP_404. Procesos vimos cómo cargar código en caliente. Hacemos los cambios oportunos en el fichero: -module(fileserver). Si salimos interrumpiendo la consola la aplicación se detendrá. 6.0.1 (^D to exit) ([email protected] para que responda a una solicitud con una URI /help que retorne un texto personalizado de ayuda. Nota Una buena práctica es que cada vez que demos una versión como terminada. RealPath = filename:join(CWD. Size/binary. Size = list_to_binary( integer_to_list(byte_size(Content)) ). "\n\n". <<"Texto de ayuda!">>). ". ". mimetype(File) -> case filename:extension(string:to_lower(File)) of ". "text/html".Ecosistema Erlang Server: Erlang Web Server Connection: Close ">>). Size/binary. Content/binary >>.icon">>. Content/binary >>. También modificamos la versión dentro del fichero webserver. ".html" -> <<"text/html">>.js" -> <<"application/javascript">>. _} -> ?RESP_404 end end. ".src para que refleje el cambio de versión y la versión en el fichero reltool. _ -> <<"text/plain">> end. "/" ++ Path -> {ok.jpeg" -> <<"image/jpeg">>. CWD} = file:get_cwd(). ".jpg" -> <<"image/jpeg">>. Path). << ?RESP_200/binary. "\nContent-lenght: ".css" -> <<"text/css">>. send(Request) -> case proplists:get_value(path. case file:read_file(RealPath) of {ok. "\nContent-lenght: ". -define(RESP_200. {error.xml" -> <<"application/xml">>. Content} -> Size = list_to_binary( integer_to_list(byte_size(Content)) ). Request. ".microsoft.1 200 OK Server: Erlang Web Server Connection: Close Content-type: ">>).png" -> <<"image/png">>.app. Type = mimetype(Path). ". ".ico" -> <<"image/vnd.0. "\n\n". "/") of "/help" -> Content = ?HELP_TEXT. Type/binary.cfg para que sea 2. <<"HTTP/1. -define(HELP_TEXT.zip" -> <<"application/zip">>. 147 .htm" -> <<"text/html">>. ". << ?RESP_200/binary.0 en lugar de 1. Este fichero se crea en la ruta webserver/lib/webserver-2. Estos ficheros se generan por aplicación y contienen información sobre los cambios que hay que realizar en caliente. Cada opción a llevar a cabo tendrá forma de tupla.fileserver} ]} ]}. 148 . [ {load_module.0.0".0.0 a la versión 1.0. Bloque de las acciones a llevar a cabo en caso de querer realizar una marcha atrás de la versión 2. [ {load_module. Es ahora cuando generamos los ficheros appup.0". La acción load_module se refiere a la recarga del módulo que se indica (en este caso fileserver).0".0 y otro con la versión 2. Uno con la versión 1. [ {"1.0/ebin. La versión que va a ser instalada. Su forma es: {"2.appup.Ecosistema Erlang Volvemos a generar el proyecto compilando y generando el producto final: $ rebar clean compile $ cd rel $ rebar generate Tenemos dos directorios de nuestro proyecto. Bloque que indica las acciones a llevar para pasar de la versión 1.0 a la 2. [ {"1. Dejamos que rebar generate-appups nos genere todos los ficheros necesarios: $ rebar generate-appups previous_release=webserver_old ==> rel (generate-appups) Generated appup for webserver Appup generation complete El fichero generado para nuestra aplicación es webserver.fileserver} ]} ]. Ahora generamos el paquete de la nueva versión. Ejecutamos: $ cp webserver_2. undefined}]}.0 Unpacked Release "2.0.gz webserver_old/releases $ webserver_old/bin/webserver upgrade webserver_2.html http://www. El fichero debe de copiarse en la ruta webserver/releases.tar.erl por otros módulos diferentes. webserver}.org/doc/man/sys. recargamos el manejador. Si los cambios han sido más significativos como la agregación o eliminación de módulos se pueden emplear otros comandos como add_module y/o delete_module.0" Installed Release "2. enviamos el código de cambio para 13 el manejador y eliminamos el módulo antiguo.0 upgrade package created El resultado será un fichero webserver_2. fileserver} Esta forma se aplicaría cuando cambiamos fileserver. El soporte para system_code_change/4 debe de existir en el módulo webserver. Por ejemplo: {add_module.org/doc/man/appup.0" Made Release "2. {load_module. [{webserver. Para ello utilizamos el comando rebar generate-upgrade de la siguiente forma: $ rebar generate-upgrade previous_release=webserver_old ==> rel (generate-upgrade) webserver_2. PostPurge y DepMods de las formas completas de las tuplas de comandos que 12 pueden verse en la web oficial de appup . {add_module. {delete_module.0" Permanent 12 13 http://www. Si hemos lanzado el código antiguo que está en webserver_old. filesystem}. copiamos dentro de su directorio releases este fichero.0. El sistema también permite trazar la dependencia de módulos y el orden en el que se deben de ir cargando a través de las opciones PrePurge. así como la información de cada aplicación y los ficheros para recargar cada una de manera adecuada.erlang.tar. ftp}.Ecosistema Erlang Nota El fichero appup permite muchos más comandos. Primero cargamos los nuevos. Este fichero contiene los binarios del programa para ejecutarse en producción.erlang.html#Mod:system_code_change-4 149 .gz. {code_change. 0 catch error:badarg -> main([]) end. La función main/1 siempre recibe una lista de cadenas de texto. Es recomendable emplear sname y solo indicar el nombre del nodo eliminando el nombre de la máquina. fact(N) -> N * fact(N-1). io:format("~p! = ~p~n". 1. fact(1) -> 1. fact(Number)]). Nota La primera línea es conocida como shebang o hashbang. Guiones en Erlang Los guiones son un tipo de programación en la que se desarrolla un código de forma rápida para servir de guión a una tarea automatizada.args dentro del directorio files antes de realizar la generación del producto final. Si accedemos mediante navegador web a la URI /help veremos que el código se ha actualizado y muestra ahora el nuevo texto de ayuda que hemos agregado. Erlang permite la realización de este tipo de guiones a través de escript.Ecosistema Erlang Importante Para que el sistema no falle habrá que revisar la configuración del nombre de nodo en el fichero vm. [Number. main([NumberTxt]) -> try Number = list_to_integer(NumberTxt). Indica el comando con el que hay que ejecutar ese guión. 7. monitorización y parada de aplicaciones. Si no hay argumentos la lista está vacía. El comando escript interpreta de forma rápida códigos Erlang que deben constar de una función main/1. Por ejemplo: #!/usr/bin/env escript main([]) -> io:format("Usage: fact <number>~n~n"). 150 . Dentro de las tareas más usuales de los guiones se encuentran el lanzamiento. Normalmente por un administrador de sistemas. fact(N) -> N * fact(N-1). {vsn. inets ]} ]}. "1. {applications. io:format("~p! = ~p~n". fact./fact 12 12! = 479001600 El comando rebar escriptize se encarga tomar todos los módulos compilados de un proyecto e introducirlos en un fichero binario y ejecutable. main([NumberTxt]) -> try Number = list_to_integer(NumberTxt). 14 Aunque hay versiones de rebar en las que la ayuda no lo muestra existe y funciona en esas mismas funciones. "Factorial"}. main([]) -> io:format("Usage: fact <number>~n~n"). 151 . Aún sin estar compilado puede hacer uso de cualquier librería de Erlang además de todos los ejemplos de código vistos anteriormente. Esto permite la distribución fácil de ese script y su instalación en el sistema de ficheros. 14 Agregamos también el fichero de aplicación siguiente: {application.Ecosistema Erlang En el código no existe declaración de módulo ni exportación de funciones. [Number. 0 catch error:badarg -> main([]) end.erl que empleamos cambiándolo de esta forma: -module(fact). fact(Number)]). Si creamos una estructura de aplicación normal con su directorio src con el fichero fact./fact Usage: fact <number> $ .[ kernel. Podemos probarlo así: $ chmod +x fact $ .0"}./fact a Usage: fact <number> $ . stdlib. -export([main/1]). 1. fact(1) -> 1. [ {description. un saludo y suerte con el código. Espero que el libro haya resultado útil. En esta primera parte hemos visto todo lo necesario para conocer el lenguaje y el funcionamiento de la máquina virtual de Erlang. Sin embargo en los proyectos profesionales de Erlang se emplea y con mucha frecuencia OTP. 152 . pero ese conocimiento debe ser ampliado a través del estudio de OTP para brindar mejores soluciones al código escrito. Hemos repasado cómo trabajar con los proyectos. ameno y que la curiosidad despertada por Erlang haya sido satisfecha e incluso las ganas de seguir aprendiendo con el siguiente volumen de este libro. Lo aprendido a lo largo de estas páginas constituye una base de conocimiento y una forma de trabajo con el lenguaje. Hasta entonces. El camino a OTP Como ya avancé en la introducción este libro consta de dos partes. 8.erl ==> fact_scriptize (escriptize) Si realizamos la prueba que hicimos con el guión anterior veremos que se comporta exactamente igual. Hemos formado nuestra mente a un nuevo conocimiento y a una nueva forma de hacer las cosas.Ecosistema Erlang Podemos crear nuestro guión compilado en forma de binario: $ rebar compile escriptize ==> fact_scriptize (compile) Compiled src/fact. dependencias y que el código se encuentra ya compilado por lo que es más rápido que en el ejemplo anterior. Esta forma tiene la ventaja de que se pueden incrustar más módulos. Apéndices . La descarga para Windows se puede realizar desde la web de descargas de la página oficial de Erlang.erlang. La instalación en estos sistemas se divide en varios pasos.html También se encuentra la versión de 64 bits para los que tengan sistemas Windows de 64 bits. tanto si lo queremos hacer desde paquetes listos para funcionar directamente o desde código fuente. Instalación en Windows Aunque siempre recomiendo GNU/Linux o incluso algún sistema BSD para programar y desarrollar software. 154 . Se seleccionan los paquetes a instalar y la ruta: 2 1 2 3 http://erlang.org/download. Las versiones oficiales 1 se ofrecen desde la página web oficial de Erlang .Apéndice A. 1. las preferencias de cada uno son distintas y hay muchos usuarios y programadores que prefieren Windows a cualquier otro sistema operativo. En este apéndice veremos como bajar e instalar Erlang. Instalación de Erlang Tener la máquina virtual de Erlang operativa con todas sus características es bastante fácil gracias a la gran cantidad de instaladores y distribuciones preparadas que existen en la web. Se trata de un instalador que nos guiará paso a paso en la instalación.org/ http://www. Entre los paquetes que hay para descargar 3 se puede encontrar Windows Binary File . Instalación en sistemas GNU/Linux La mayoría de distribuciones GNU/Linux disponen de sistemas de instalación de paquetes de forma automatizada. En un futuro se plantea la eliminación de estas librerías en favor de otras de mingw (una versión de GCC para Windows) que nos permitirán saltar esta parte. Podemos tomar cualquier ejemplo del libro para probar su funcionamiento.Instalación de Erlang Nota La instalación de la versión R12B02 requiere de la instalación de unas DLLs que son propiedad de Microsoft. El instalador inicia un proceso de instalación para estas librerías en las que habrá que aceptar las licencias y acuerdos de uso de las propias librerías. Seguimos con la instalación hasta que el instalador nos informe de que ha finalizado con éxito. Podemos lanzar el que tiene como título Erlang con el logotipo del lenguaje a su lado para que se muestre la siguiente pantalla: Ahora ya tenemos lista la consola de Erlang. 2. Erlang está disponible por defecto en la mayoría de estas distribuciones pero. dado que estos paquetes en muchas de estas distribuciones se marcaron como estables 155 . En el menú de inicio veremos que se ha creado un nuevo grupo de programas. 2.gz tar xzf otp_src_R15B02. 2. Para realizar la acción anterior sin que surjan problemas. En este caso habrá que descargar el último archivo comprimido de código: # # # # # wget http://www. En su lugar se debe de acceder a root a través del comando sudo. Los paquetes actuales se pueden descargar desde la web de Erlang Solutions: https://www.erlang-solutions.Instalación de Erlang hace mucho tiempo las versiones de Erlang disponibles pueden ser algo antiguas.com/downloads/download-erlang-otp Las versiones para CentOS y Fedora se descargan en formato RPM y pueden instalarse a través de la herramienta rpm.erlang.1. deberemos de ejecutar antes: sudo su. El comando configure nos dará pistas sobre las librerías que haya que instalar. 156 . Ubuntu y Raspbian se descargan en formato DEB y pueden instalarse a través de la herramienta dpkg.2.tar.tar. Importante Sistemas como Ubuntu no disponen acceso directo como usuario root. Las versiones para Debian. Una vez instalado podemos ejecutar desde consola el comando erl o erlc entre otros.gz cd otp_src_R15B02 .org/download/otp_src_R15B02. 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 de varias versiones instaladas en rutas diferentes a las que se establecen por defecto. Desde Paquetes Binarios Tenemos la opción de descargar un paquete actualizado e instalarlo en lugar del que provee por defecto la distribución que estemos usando./configure make && make install La compilación requiere que se disponga en el sistema de un compilador y las librerías en las que se basa Erlang. 4 http://www. En este sistema podemos optar por instalar este 4 paquete o por la instalación desde otros sistemas como MacPorts .org/ 157 . En sistemas como OpenSolaris o BSD (FreeBSD.Instalación de Erlang 3.2.macports. “Compilando el Código Fuente” . Otros sistemas La empresa Erlang Solutions provee paquetes de instalación para otros sistemas como MacOS X. OpenBSD o NetBSD) la solución más común es instalar desde código fuente tal y como se comentó en la sección Sección 2. no es posible emplearlos en el código de un programa convencional. miss. El lenguaje. En este apéndice veremos las opciones que nos ofrece este intérprete de comandos para facilitar la tarea de gestión. 158 . Erlang como máquina virtual dispone de esta línea de comandos para facilitar su gestión y demostrar su versatilidad permitiendo conectar una consola a un nodo que se encuentre en ejecución y permitir al administrador obtener información del servidor en ejecución. En la consola se pueden gestionar los registros a través de las siguientes funciones: rd(R. La primera forma elimina todos los registros mientras que la segunda solo elimina el registro pasado como parámetro R. error}). {hits. Registros Los registros se comentaron en la Sección 1.Apéndice B. 1.6. rl() / rl(R) Muestra todos los registros definidos en la línea de comandos en el primer caso y solo el registro pasado como parámetro en el segundo caso. La línea de comandos El código de la mayoría de ejemplos del libro han sido desarrollados en la consola o línea de comandos. La definición se muestra como se escribiría dentro de un fichero de código. Importante Las funciones que se listan a continuación están disponibles solo en la línea de comandos. Muchas de estas funciones ya se han ido mostrando a través de los capítulos del libro por lo que este compendio será una referencia útil para nuestro trabajo del día a día. rf() / rf(R) Elimina la definición de los registros cargados. La línea de comandos es por tanto uno de los principales elementos de Erlang. “Registros” del Capítulo 2.D) Define un registro en la línea de comandos: > rd(state. carga e información para los módulos: c(FoM) Compila un fichero pasando su nombre como parámetro. Procesos. Se muestra información como la fecha y hora de compilación. la ruta de dónde se encuentra el módulo en el sistema de ficheros.R) / rr(MoW. el fichero pasado como parámetro debe de ser un fichero con sintaxis válida para Yecc. 1 Las opciones que se pueden usar con rr/3 son las mismas que se pueden emplear para la compilación.O) Carga los módulos de uno o varios ficheros. 2. lc([F]) Lista de ficheros a compilar. opcionalmente con su ruta. Los ficheros se pueden indicar mediante el nombre de un módulo (véase m()) o el nombre de un fichero o varios con el uso de comodines (wildcard). 159 . m() / m(M) Muestra todos los módulos cargados en memoria en el primer caso e información detallada del módulo cargado en el segundo caso. Se puede agregar un segundo parámetro que indique el registro que se desea cargar y un tercer parámetro que se usará como conjunto 1 de opciones .R. l(M) Permite cargar un módulo. Conviene recordar lo ya mencionado sobre la carga de módulos en la Sección 8. y(F) Genera un analizador Yecc. El nombre proporcionado será un átomo con el nombre del módulo o una cadena que indique el nombre del fichero. las funciones que exporta y las opciones de compilación. “Recarga de código” del Capítulo 5. nc(FoM) Compila y carga el módulo o fichero en todos los nodos conectados.La línea de comandos rr(Modulo) / rr(Wildcard) / rr(MoW. nl(M) Carga el módulo indicado en todos los nodos conectados. Módulos Indicaremos todos los comandos referentes a la compilación. El símbolo del sistema (o prompt) nos indicará el número de orden que estamos ejecutando. Estas variables tienen el comportamiento de única asignación igual que el código que podemos escribir en cualquier módulo. la consola de Erlang también almacena los últimos resultados. h() Muestra el histórico de comandos ejecutados. 4. 160 . history(N) Configura el número de entradas que serán almacenadas como histórico. Histórico La consola dispone de un histórico que nos permite repetir comandos ya utilizados en la consola. results(N) Configura el número de resultados que serán almacenados como histórico. El número de resultados almacenados también es configurable. El histórico es configurable y contendrá los últimos comandos tecleados.La línea de comandos 3. Estas son las funciones que pueden emplearse: e(N) Repite el comando con orden N según el símbolo de sistema de la consola. Además de los comandos. f() / f(X) Indica a la línea de comandos que olvide (forget) todas las variables o solo la indicada como parámetro. Variables En la línea de comandos se pueden emplear variables. Las siguientes funciones nos permiten gestionar estas variables: b() Muestra todas las variables empleadas o enlazadas (binding) a un valor en la línea de comandos. Cuando se lanza una excepción el evaluador es regenerado por el proceso de la consola.Z) Obtiene el tipo de dato PID de los números dados. i() / ni() Muestra todos los procesos del nodo o de todos los nodos conectados respectivamente. 5. el diccionario del proceso y memoria utilizada entre otras opciones más.Y. i(X. A diferencia de e(N) el comando no se vuelve a ejecutar. la cola de mensajes. Esto provoca que se pierdan tablas ETS entre otras cosas. La información obtenida es el estado de ejecución del proceso. catch_exception(B) Cada ejecución se realiza mediante un evaluador.Z) Muestra información de un proceso dando sus números como argumentos separados de la función. flush() Muestra todos los mensajes enviados al proceso de la consola. 161 . Si ejecutamos esta función con true el evaluador captura la excepción y no muere. regs() / nregs() Lista todos los procesos registrados (con nombre) en el nodo actual o en todos los nodos conectados respectivamente. Procesos Estas son funciones rápidas y de gestión sobre los temas que ya se revisaron en el Capítulo 5. Procesos: bt(Pid) Obtiene el trazado de pila del proceso en ejecución.La línea de comandos v(N) Obtiene el resultado de la línea correspondiente pasada como parámetro. pid(X.Y. procesos a los que está enlazado. solo se muestra el resultado del comando N ejecutado anteriormente. 162 . Es útil cuando se quiere interrumpir un bucle infinito sin perder las variables empleadas. Las siguientes funciones nos ayudan en esta y otras tareas relacionadas: cd(Dir) Cambia el directorio de trabajo. i [nn] Detiene la consola actual o la que corresponda al número que se indique como argumento. Este modo nos permite gestionar estas consolas. conectarnos a una consola remota. pwd() Imprime el directorio de trabajo actual.La línea de comandos 6. Importante Cada trabajo que se lanza es una consola (shell). Este modo nos permite lanzar una nueva consola. Cada nodo puede tener tantas consolas como se quiera. Esta consola es denominada JCL (Job Control Mode o modo de control de trabajos). Modo JCL Cuando se presiona la combinación de teclas Control+G se accede a una nueva consola. detener una consola en ejecución o cambiar de una a otra consola. Estos son los comandos que podemos emplear en este modo: c [nn] Conectar a una consola. 7. k [nn] Mata la consola actual o la que corresponda al número que se indique como argumento. Directorio de trabajo En cualquier momento podemos modificar el directorio de trabajo dentro de la consola. Si no se especifica un número vuelve al actual. ls() / ls(Dir) Lista el directorio actual u otro indicado como parámetro de forma relativa o absoluta a través de una lista de caracteres. Se indica una lista de caracteres con la ruta relativa o absoluta para el cambio. ejecutando la función de consola q() o a través del modo JCL y su comando q. La consola actual se indicará con un asterisco (*). Se lanza una consola en ese nodo y queda visible en el listado de consolas. 8. Si se indica el nombre de un módulo como argumento se intentará lanzar un proceso con ese módulo como consola alternativa. q Finaliza la ejecución del nodo Erlang en el que estemos ejecutando el modo JCL. Salir de la consola Para salir de la consola hay varias formas. Se puede salir presionando dos veces la combinación de teclas Control+C. s [shell] Inicia una consola nueva. 163 . r [node [shell]] Indica que deseamos crear una consola en un nodo al que se tiene conexión. Se puede indicar también una consola alternativa en caso de disponer de ella.La línea de comandos j Lista las consolas en ejecución. i/1 y otras funciones como exit/1 integradas en una única interfaz. pman Process Manager o administrador de procesos. 1.Apéndice C. Esta aplicación nos proporciona una ventana con botones de acceso directo a las herramientas de la interfaz gráfica de Erlang. Barra de herramientas Para facilitar la tarea de acceder al conjunto de herramientas gráficas disponemos de toolbar. Se emplea para poder visualizar el contenido de las tablas ETS y las que maneja la base de datos Mnesia. La podemos lanzar de la siguiente manera: > toolbar:start(). 164 . Procesos tratamos la forma en la que listar los procesos de consola. En el capítulo Capítulo 5. debugger El depurador nos permite seguir la ejecución de un código en la ventana de proceso y revisar los datos de sus variables en ese momento. Es la versión gráfica de lo que conseguimos con las funciones de consola i/0. Herramientas gráficas Erlang es una máquina virtual además de un lenguaje por lo que requiere de herramientas que le permitan gestionar sus procesos de una forma fácil para el usuario. Se abrirá una ventana como la siguiente: Los cuatro botones que se pueden observar en la imagen son (de izquierda a derecha): tv Table Visualizer o visor de tablas. así como las tablas ETS y Mnesia y la depuración de los procesos que ejecutemos. Ahora veremos la forma de ver estos procesos de forma gráfica. Esta relación de procesos es la que se monitoriza bajo el nombre propio de cada aplicación que se ejecuta. Nota En los menús podemos ver opciones como Add GS contributions que agrega otros cuatro botones extra: el juego bonk. Ecosistema Erlang. El concepto de aplicación proviene de OTP y se comentó en el Capítulo  8.Herramientas gráficas appmon Application Monitor o monitor de aplicaciones. El código fuente de estas aplicaciones está disponible junto con el código fuente de Erlang. en el directorio: otp_src_R15B02/lib/gs/contribs 2. el juego othello y el juego Cols. mandelbrot que es un generador de fractales. Cada proceso se identifica con su nombre registrado o con su PID en caso de no disponer de nombre. Permite ver la carga que supone en el nodo la aplicación y el árbol de dependencia de procesos entre otras opciones. Monitor de aplicaciones El monitor de aplicaciones nos proporciona información sobre las aplicaciones ejecutadas en la máquina virtual de Erlang. 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. Sobre cada proceso podemos ejecutar una serie de acciones que se representan con los botones superiores que se pueden ver en la ventana: 165 . Estas aplicaciones son botones que si se presionan nos muestran una jerarquía de procesos. La aplicación al lanzarse genera un proceso principal que puede estar enlazado con otros. hacerles ping para volver a conectarlos. Las acciones son: Info Se abrirá una nueva ventana que mostrará la información del proceso. Es equivalente a: Pid ! Mensaje Trace Cada mensaje recibido al proceso marcado es impreso por la consola.Herramientas gráficas La barra superior de botones es como una barra de herramientas. Estas opciones son útiles cuando se emplea la herramienta con otros nodos. Stop y Ping. Restart. ya que permite reiniciar estos nodos y cuando se vean como desconectados. Siempre hay un botón que aparece como marcado indicando así la opción que se hará sobre cualquier proceso cuando se haga clic sobre él. Kill Envía la señal de finalización al proceso. Send Permite enviar un mensaje al proceso. Equivalente a exit/1. El menú de la ventana principal dispone además de otras opciones en su menú Actions como: Reboot. Es equivalente a la función erlang:trace/3. 166 . sino que también puede ver el estado de las aplicaciones de otros nodos a los que se encuentre conectado. donde se listarán todos los nodos a los que esté conectada la máquina virtual de Erlang. visualizando en ella los mensajes entrantes al proceso que se esté trazando. Nota El gestor de procesos no solo puede visualizar y gestionar los procesos del nodo en el que fue lanzado. 3. 167 . Esto es posible a través del menú Nodes. donde se listarán todos los nodos a los que esté conectada la máquina virtual de Erlang. las reducciones aplicadas y el tamaño que ocupa el proceso. Esta interfaz permite activar el trazado de procesos en una ventana aparte. Esto es posible a través del menú Nodes. la función actual que están ejecutando. 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.Herramientas gráficas Nota El monitor de aplicaciones no solo puede visualizar el estado de las aplicaciones del nodo en el que fue lanzado. Para trazar un proceso solo hay que seleccionarlo y a través del menú Trace seleccionar la opción Trace selected process. nombre registrado (si tienen). sino que también puede ver y gestionar los procesos de otros nodos a los que se encuentre conectado. los mensajes que tienen pendientes en el buźon. El gestor de procesos nos permite visualizar de forma rápida y en tiempo real los procesos que se están ejecutando en la máquina virtual de Erlang su PID. PID del propietario de la tabla. sino que también puede hacer lo mismo con otros nodos a los que se encuentre conectado. se abrirá una segunda ventana en la que se mostrará el contenido de la tabla ETS seleccionada. ver tablas del sistema o no legibles son algunas de las opciones. nombre registrado del proceso propietario (si tiene) y tamaño de la tabla (en número de entradas). Nota El visualizador de tablas no solo puede visualizar y gestionar las tablas ETS y Mnesia del nodo en el que fue lanzado. A través del menú View se puede conmutar entre la visualización de las tablas ETS y las tablas Mnesia. donde se listarán todos los nodos a los que esté conectada la máquina virtual de Erlang.Herramientas gráficas 4. Por defecto nos muestra un listado de las tablas ETS que hay creadas en el nodo. Si hacemos doble clic sobre cualquier entrada de la tabla principal. 168 .. Visor de tablas Las tablas ETS y las que se crean con la base de datos de Mnesia están disponibles para su visualización a través de esta interfaz. Se visualiza como si fuese una hoja de cálculo y se marca sobre las columnas con el símbolo de una llave cuál es la clave primaria de la tabla. Esto es posible a través del menú File. La información que se muestra en la tabla principal es: nombre de la tabla. identificador de la tabla.. En el menú Options hay opciones referentes a la visualización de las tablas en el listado: refrescar. opción Nodes.. Esta es una ventana de visualización y edición por lo que podemos seleccionar las entradas de la tabla y editarlas o eliminarlas a través de las opciones disponibles en el menú Edit. Herramientas gráficas 5. Observer El programa observer es una unificación de pman, tv y appmon además de otras características más en un entorno wxWindow mejorado con respecto a los anteriores. Podemos lanzar este programa de la siguiente manera: > observer:start(). La ventana abierta dispone de un conjunto de pestañas o lengüetas que disponen de todas las funcionalidades del conjunto de programas vistos en las secciones anteriores: System Ofrece información del sistema: versión y arquitectura de la máquina virtual de Erlang, CPUs e hilos en ejecución, uso de la memoria y estadísticas de uso. Load Charts Se presentan tres gráficos en tiempo real: utilización del programador de tareas (equivalente a la carga del procesador), uso de la memoria y uso de la Entrada/Salida. Applications Ofrece la misma información que appmon. Visualiza el listado de aplicaciones a la izquierda y el árbol de procesos a la derecha, previa selecció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, mensajes encolados y función en ejecución actualmente. Table Viewer Lista las tablas ETS y Mnesia como lo hace la aplicación tv. A diferencia de tv, observer no permite la creación de nuevas tablas ETS aunque sí permite modificar y/o eliminar entradas de las tablas existentes. Trace Overview Tanto la aplicación appmon como pman permiten trazar procesos. La aplicación observer unifica esto en una sola pestaña cambiando la forma en la que realizar las trazas de los procesos. 169 Herramientas gráficas 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 se encuentre conectado. Esto es posible a través del menú Nodes, donde se listarán todos los nodos a los que esté conectada la máquina virtual de Erlang además de dar la posibilidad de conectar con otros nodos que no se encuentren en el listado. 6. Depurador Esta es quizás la herramienta más importante y que mejor se debería de aprender a utilizar para poder analizar el código realizado de una forma más cercana al dato. Aunque las trazas serán suficientes en algunos casos, siempre es mejor depurar un código que nos origina un error que no conseguimos entender bien que abarcar el problema al modo pruebaensayo-error. El depurador se lanza desde consola así: > debugger:start(local). La ventana que se abre tendrá esta forma: 170 Herramientas gráficas 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 el propio menú Module debe de agregarse después una opción con el nombre del módulo cargado. Tras esto, presionamos los tres cuadros de verificación visibles bajo el cuadro de la ventana donde aparece el nombre del módulo: First Call, On Break y On Exit. Con esto conseguimos que el depurador se active cuando se suceda cualquiera de estos tres eventos sobre el módulo. En la consola de Erlang ejecutamos una función del módulo. Vemos que se 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. 171 Nota El depurador se puede lanzar en dos modos: local o global. la llamada que los inició. ocioso u otro) e información sobre la ejecución del proceso. No es aconsejable lanzar el depurador en un cluster de más de dos nodos. el contenido de las variables y otros aspectos que nos ayuden a aprender a utilizar bien esta herramienta.html 172 . ya que la ejecución concurrente de un mismo módulo en varios nodos al mismo tiempo podría llevar a un funcionamiento inconsistente. probar opciones de compilación. el estado del proceso (ejecución. el nombre del proceso.org/doc/apps/debugger/debugger_chapter. la evaluación de expresiones. Durante la ejecución la ventana principal mostrará la información de los procesos que hay en ejecución. Para más información sobre la misma: http://www.Herramientas gráficas La parte inferior de la ventana muestra un listado de las variables del contexto de la función que se está depurando a la derecha. En la parte izquierda hay un evaluador que permite escribir expresiones simples para comprobación de datos. Por defecto se lanza de modo global. por lo que cualquier módulo que se interprete será mostrado en la ventana del monitor. ver los procesos.erlang. Tomando ejemplos de los últimos capítulos podemos hacer pruebas de ejecución del código. 173 . Documents Similar To erlang-iSkip carouselcarousel previouscarousel nextMetodología de la programaciónLenguaje Elixir Con VM ErlangTarea3Software de La ComputadoraInvestigacion PlataformasDistribución ErlangDistribucion ErlangTrabajo Distribucion ErlangDistribucion ErlangJava7-Lenguajesdinamicosmde-jcabot-110407060603-phpapp01Manual de ALGORSoftware 1Visual Basic Para Excel en La Ensenanza de La Inge1Programación de Computadores. Mayo de 2008Practica de ores 2011b6 Pasos Para El Buen Análisis de DatosAnalisis-automatico-de-textos-en-espanol-.pdfpartituras-de-interaccion.pdf5. Tipos de SoftwareCap1_introduccion a La ProgramacionEl Lenguaje PHP4 Bioinformatica.pdfTipos de SoftwareTema 2 Estructura SecuencialHistoriaLenguajesConceptos Basicos de Programación Camila Salinas scurso cintroCsharp_version099zzSentencia en PhytonMore From KarelBRGSkip carouselcarousel previouscarousel nextFontforge TutorialThe 6502 in Terminatoriscii91Robots ElColombianonasmdocMorse Codesx86-6464bitPerfComo Crear Tus Propias Fuentes Sin Programa14 de marzo el día del número PiEl punto G y otras paradas en la geografía femeninaPFC - Fernando Valera GuardiolaGas EncyclopediaUnicode for Indian LanguagesQué fragancias universales nos atraen másZiv Lempel 1978 Variable-rateRFC 3092Formatos ExcelQR Code SecuritycompresiondatosWineOrdenadores Zombis03 Codigo de HuffmanVideo File Format Spec v10 1Langdon Run-Length EncodingsQR Code IntroductionMecánicas Matricial y OndulatoriaZiv Lempel 1977 Universal AlgorithmIncompatibilities ES3 and ES4Iqr Code Qrcode_com Denso WaveFooter MenuBack To TopAboutAbout ScribdPressOur blogJoin our team!Contact UsJoin todayInvite FriendsGiftsLegalTermsPrivacyCopyrightSupportHelp / FAQAccessibilityPurchase helpAdChoicesPublishersSocial MediaCopyright © 2018 Scribd Inc. .Browse Books.Site Directory.Site Language: English中文EspañolالعربيةPortuguês日本語DeutschFrançaisTurkceРусский языкTiếng việtJęzyk polskiBahasa indonesiaSign up to vote on this titleUsefulNot usefulYou're Reading a Free PreviewDownloadClose DialogAre you sure?This action might not be possible to undo. Are you sure you want to continue?CANCELOK
Copyright © 2024 DOKUMEN.SITE Inc.