Translate
domingo, 19 de mayo de 2013
Vistazo a Erlang
Erlang no es solo un lenguaje también es una tecnología; dado que cuenta con maquina virtual, un middleware OTP y librerías. Muchas empresas utilizan erlang; pero tal vez se hizo famoso porque se utilizo en el desarrollo de la base de datos NoSQL llamada couchDB.
Erlang es un lenguaje funcional con lo que eso significa por lo tanto tiene transparencia referencial y es declarativo. Erlang fue pensado como un lenguaje que debe correr de forma concurrente y distribuida; eso explica su diseño. A la vez esta basado en convenciones lo que hace que este lenguaje sea muy practico.
Erlang es software libre y se encuentra en los repositorios de linux por lo tanto se puede instalar de la siguiente manera en distribuciones basadas en debian:
apt-get install erlang
En rhel:
yum install erlang
Si vamos a una terminal y ponemos erl vamos a abrir el interprete de erlang. Con el podemos sumar, multiplicar, dividir, etc :
1> 2 + 2.
4
2> (4 + 5) * 2.
18
3> (12 + 3) div 5.
3
4> (12 + 3) / 5.
3.00000
En estos ejemplos podemos ver que el delimitador de las sentencias es el “.” como smalltalk. A la vez se puede ver la diferencia de / con div que div nos de vuelve un valor entero. Podemos seguir jugando con la consola:
4> 'Hola mundo'.
'Hola mundo'
5> 1==2.
false
6> not ((1<3) and (2==2)).
false
7> tuple_size({1,{1,2,3},2})
3
Bueno en los siguientes ejemplos podemos ver como se manejan booleanos, similar que otros lenguajes y también podemos ver un ejemplo de tuplas, las tuplas son un conjunto de elementos de igual o distinto tipo similares a las Listas. Las listas en erlang se definen con los corchetes [] vemos ejemplos:
8> []
[]
9> [1, 2, [2, “hola”, 5], 4]
[1, 2, [2, “hola”, 5], 4]
En Erlang los string son listas de caracteres que se pueden definir con “”. Es decir que “” es igual a []. Los caracteres son representados por medio del simbolo $ y también con el numero del carácter.
10> [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
“Hello Word”
11> [$H, $e, $l, $l, $o, $ , $W, $o, $r, $d]
“Hello Word”
Erlang tienen un conjunto muy amplio de funciones con listas:
12> lists:max([1, 2, 3])
3
13> lists:reverse([1, 2, 3])
[3,2,1]
13> lists:sort([1, 3, 2])
[1, 2, 3]
14> lists:sum([1, 2, 3, 4])
10
15> [1, 2, 3, 4] ++ [5, 6, 7]
[1, 2, 3, 4, 5, 6, 7]
16> [1, 2, 3, 4] -- [3, 4]
[1, 2]
Y eso es todo, los deje con ganas de más?
Dejo link:
http://www.erlang.org/
viernes, 15 de septiembre de 2023
¿Qué es Erlang?
Erlang es un lenguaje de programación funcional. Si alguna vez ha trabajado con lenguajes imperativos, declaraciones como i++ pueden resultarle normales; en programación funcional no están permitidos. De hecho, ¡está estrictamente prohibido cambiar el valor de cualquier variable! Esto puede sonar extraño al principio, pero si recuerdas tus clases de matemáticas, en realidad así es como las aprendiste:
y = 2
x = y + 3
x = 2 + 3
x = 5
Si hubiera agregado lo siguiente:
x = 5 + 1
x = x
∴ 5 = 6
Habrías estado muy confundido. La programación funcional reconoce esto: si digo que x es 5, entonces lógicamente no puedo afirmar que también sea 6. Esto sería deshonesto. Esta es también la razón por la que una función con el mismo parámetro siempre debería devolver el mismo resultado:
x = add_two_to(3) = 5
∴ x = 5
Las funciones que siempre devuelven el mismo resultado para el mismo parámetro se denominan transparencia referencial. Es lo que nos permite reemplazar add_two_to(3) con 5, ya que el resultado de 3+2 siempre será 5. Eso significa que luego podemos unir docenas de funciones para resolver problemas más complejos mientras nos aseguramos de que nada se rompa. Lógico y limpio ¿no? Sin embargo, hay un problema:
x = today() = 2009/10/22
-- wait a day --
x = today() = 2009/10/23
x = x
∴ 2009/10/22 = 2009/10/23
¡Oh, no! ¡Mis hermosas ecuaciones! ¡De repente todos se equivocaron! ¿Cómo es que mi función arroja un resultado diferente cada día?
Evidentemente, hay algunos casos en los que resulta útil romper la transparencia referencial. Erlang tiene este enfoque muy pragmático con la programación funcional: obedece sus principios más puros (transparencia referencial, evitar datos mutables, etc.), pero aléjate de ellos cuando surgen problemas del mundo real.
Ahora, definimos Erlang como un lenguaje de programación funcional, pero también hay un gran énfasis en la concurrencia y la alta confiabilidad. Para poder realizar docenas de tareas al mismo tiempo, Erlang utiliza el modelo de actor, y cada actor es un proceso separado en la máquina virtual. En pocas palabras, si fueras un actor en el mundo de Erlang, serías una persona solitaria, sentada en una habitación oscura sin ventanas, esperando junto a tu buzón para recibir un mensaje. Una vez que recibes un mensaje, reaccionas de una manera específica: pagas las facturas al recibirlas, respondes a las tarjetas de cumpleaños con una carta de agradecimiento e ignoras las cartas que no puedes entender.
El modelo de actor de Erlang puede imaginarse como un mundo en el que todos están sentados solos en su propia habitación y pueden realizar algunas tareas distintas. Todos se comunican estrictamente escribiendo cartas y listo. Si bien suena como una vida aburrida (y una nueva era para el servicio postal), significa que puedes pedirle a muchas personas que realicen tareas muy específicas por ti, y ninguna de ellas hará algo mal o cometerá errores que tendrán repercusiones en tu vida. el trabajo de otros; es posible que ni siquiera conozcan la existencia de otras personas además de ti (y eso es genial).
Para escapar de esta analogía, Erlang te obliga a escribir actores (procesos) que no compartirán información con otros bits de código a menos que se pasen mensajes entre sí. Cada comunicación es explícita, rastreable y segura.
Cuando definimos Erlang, lo hicimos a nivel de lenguaje, pero en un sentido más amplio, esto no es todo: Erlang es también un entorno de desarrollo en su conjunto. El código se compila en código de bytes y se ejecuta dentro de una máquina virtual. Entonces Erlang, al igual que Java, puede ejecutarse en cualquier lugar. La distribución estándar incluye (entre otras) herramientas de desarrollo (compilador, depurador, generador de perfiles, framework de prueba), el framework Open Telecom Platform (OTP), un servidor web, un generador de analizadores y la base de datos mnesia, un sistema de almacenamiento de valores clave capaz de replicarse en muchos servidores, admitiendo transacciones anidadas y permitiéndole almacenar cualquier tipo de datos de Erlang.
La VM y las librería también le permiten actualizar el código de un sistema en ejecución sin interrumpir ningún programa, distribuir su código con facilidad en muchas computadoras y administrar errores y fallas de una manera simple pero poderosa.
Una política general relacionada en Erlang: dejar que se explote. No como un avión con decenas de pasajeros muriendo, sino más bien como un equilibrista con una red de seguridad debajo. Si bien debes evitar cometer errores, en la mayoría de los casos no será necesario verificar cada tipo o condición de error.
La capacidad de Erlang para recuperarse de errores, organizar el código con actores y hacerlo escalar con la distribución y la concurrencia suena increíble!
Dejo link: https://learnyousomeerlang.com/introduction
lunes, 13 de mayo de 2024
Tipos en Erlang
Erlang se escribe dinámicamente: cada error se detecta en tiempo de ejecución y el compilador no siempre le gritará al compilar módulos donde las cosas pueden resultar en fallas.
Un punto de fricción clásico entre los defensores de los lenguajes de tipado estático y dinámico tiene que ver con la seguridad del software que se escribe. Una idea sugerida con frecuencia es que los buenos sistemas de tipo estático con compiladores que los aplican con fervor detectarán la mayoría de los errores que esperan ocurrir antes de que puedas ejecutar el código. Como tal, los lenguajes escritos estáticamente deben considerarse más seguros que sus homólogos dinámicos. Si bien esto podría ser cierto en comparación con muchos lenguajes dinámicos, Erlang no está de acuerdo y ciertamente tiene un historial que lo demuestra. El mejor ejemplo son los nueve nueves (99,9999999%) de disponibilidad que se ofrecen en los conmutadores ATM Ericsson AXD 301, que constan de más de 1 millón de líneas de código Erlang. Tenga en cuenta que esto no es una indicación de que ninguno de los componentes de un sistema basado en Erlang haya fallado, sino que un sistema de conmutación general estuvo disponible el 99,9999999 % del tiempo, incluidas las interrupciones planificadas. Esto se debe en parte a que Erlang se basa en la noción de que una falla en uno de los componentes no debería afectar a todo el sistema. Se tienen en cuenta los errores provenientes del programador, fallas de hardware o [algunas] fallas de red: el lenguaje incluye características que le permitirán distribuir un programa a diferentes nodos, manejar errores inesperados y nunca dejar de ejecutarse.
Para abreviar, mientras que la mayoría de los lenguajes y sistemas de tipos tienen como objetivo hacer que un programa esté libre de errores, Erlang utiliza una estrategia en la que se supone que los errores ocurrirán de todos modos y se asegura de cubrir estos casos: El sistema de tipos dinámicos de Erlang no es una barrera para confiabilidad y seguridad de los programas. Esto suena como un montón de palabras proféticas, pero verás cómo se hace.
Históricamente se eligió la escritura dinámica por razones simples; Aquellos que implementaron Erlang al principio procedían en su mayoría de lenguajes escritos dinámicamente y, como tal, tener Erlang dinámico era la opción más natural para ellos.
Erlang también está fuertemente tipado. Un lenguaje débilmente tipado haría conversiones de tipos implícitas entre términos. Si Erlang tuviera un tipo débil, posiblemente podríamos hacer la operación 6 = 5 + "1". mientras que en la práctica, se lanzará una excepción:
1> 6 + "1".
** exception error: bad argument in an arithmetic expression
in operator +/2
called as 6 + "1"
Por supuesto, hay ocasiones en las que es posible que desee convertir un tipo de datos en otro: cambiar cadenas normales en cadenas de bits para almacenarlas o un número entero en un número de punto flotante. La biblioteca estándar de Erlang proporciona una serie de funciones para hacerlo.
domingo, 19 de diciembre de 2010
Erlang

lunes, 23 de diciembre de 2024
Concurrencia en Erlang parte 2
En su día, el desarrollo de Erlang como lenguaje fue extremadamente rápido y recibía comentarios frecuentes de los ingenieros que trabajaban en los conmutadores telefónicos de Erlang. Estas interacciones demostraron que la concurrencia basada en procesos y el paso asincrónico de mensajes eran una buena forma de modelar los problemas a los que se enfrentaban. Además, el mundo de la telefonía ya tenía cierta cultura que favorecía la concurrencia antes de que Erlang existiera. Esto se heredó de PLEX, un lenguaje creado anteriormente en Ericsson, y de AXE, un conmutador desarrollado con él. Erlang siguió esta tendencia e intentó mejorar las herramientas disponibles anteriormente.
Erlang tenía algunos requisitos que satisfacer antes de ser considerado bueno. Los principales eran poder escalar y dar soporte a muchos miles de usuarios en muchos conmutadores, y luego lograr una alta confiabilidad, hasta el punto de no detener nunca el código.
Escalabilidad: Me centraré primero en la escalabilidad. Algunas propiedades se consideraron necesarias para lograr la escalabilidad. Como los usuarios se representarían como procesos que solo reaccionan ante ciertos eventos (por ejemplo, recibir una llamada, colgar, etc.), un sistema ideal soportaría procesos que realizan pequeños cálculos y cambian entre ellos muy rápidamente a medida que se producen los eventos. Para que sea eficiente, tenía sentido que los procesos se iniciaran muy rápidamente, se destruyeran muy rápidamente y se pudieran cambiar realmente rápido. Para lograr esto era obligatorio que fueran livianos. También era obligatorio porque no querías tener cosas como grupos de procesos (una cantidad fija de procesos entre los que dividir el trabajo). En cambio, sería mucho más fácil diseñar programas que pudieran usar tantos procesos como necesiten.
Otro aspecto importante de la escalabilidad es poder eludir las limitaciones de tu hardware. Hay dos formas de hacer esto: mejorar el hardware o agregar más hardware. La primera opción es útil hasta cierto punto, después del cual se vuelve extremadamente costosa (por ejemplo, comprar una supercomputadora). La segunda opción suele ser más barata y requiere que agregues más computadoras para hacer el trabajo. Aquí es donde puede ser útil tener la distribución como parte de tu lenguaje.
De todos modos, para volver a los procesos pequeños, debido a que las aplicaciones de telefonía necesitaban mucha fiabilidad, se decidió que la forma más limpia de hacer las cosas era prohibir que los procesos compartieran memoria. La memoria compartida podía dejar las cosas en un estado inconsistente después de algunos fallos (especialmente en los datos compartidos entre diferentes nodos) y tenía algunas complicaciones. En cambio, los procesos deberían comunicarse enviando mensajes donde se copian todos los datos. Esto podría ser más lento, pero más seguro.
Tolerancia a fallos: Esto nos lleva al segundo tipo de requisitos para Erlang: la fiabilidad. Los primeros escritores de Erlang siempre tuvieron en cuenta que los fallos son comunes. Puedes intentar evitar los errores todo lo que quieras, pero la mayoría de las veces algunos de ellos seguirán ocurriendo. En el caso de que los errores no ocurran, nada puede detener los fallos de hardware todo el tiempo. La idea es, por tanto, encontrar buenas formas de manejar los errores y los problemas en lugar de intentar evitarlos todos.
Resulta que adoptar el enfoque de diseño de múltiples procesos con paso de mensajes fue una buena idea, porque el manejo de errores se podía incorporar en él con relativa facilidad. Tomemos como ejemplo los procesos livianos (creados para reinicios y apagados rápidos). Algunos estudios demostraron que las principales fuentes de tiempo de inactividad en sistemas de software a gran escala son errores intermitentes o transitorios (fuente). Luego, existe un principio que dice que los errores que corrompen los datos deben hacer que la parte defectuosa del sistema muera lo más rápido posible para evitar propagar errores y datos incorrectos al resto del sistema. Otro concepto aquí es que existen muchas formas diferentes para que un sistema termine, dos de las cuales son apagados limpios y fallas (que terminan con un error inesperado).
Aquí, el peor caso es obviamente la falla. Una solución segura sería asegurarse de que todas las fallas sean iguales a los apagados limpios: esto se puede hacer a través de prácticas como la no compartición de datos y la asignación única (que aísla la memoria de un proceso), evitar bloqueos (un bloqueo podría no desbloquearse durante una falla, lo que evitaría que otros procesos accedan a los datos o dejaría los datos en un estado inconsistente) y otras cosas que no cubriré más, pero que eran todas parte del diseño de Erlang. La solución ideal en Erlang es, por tanto, matar los procesos lo más rápido posible para evitar la corrupción de datos y los errores transitorios. Los procesos ligeros son un elemento clave en esto. Otros mecanismos de manejo de errores también forman parte del lenguaje para permitir que los procesos monitoreen otros procesos, con el fin de saber cuándo mueren los procesos y decidir qué hacer al respecto.
Suponiendo que reiniciar los procesos muy rápido sea suficiente para lidiar con los fallos, el siguiente problema que se presenta son los fallos de hardware. ¿Cómo se asegura de que su programa siga funcionando cuando alguien patea la computadora en la que se está ejecutando? La idea es simplemente que el programa se ejecute en más de una computadora a la vez, algo que era necesario para escalar de todos modos. Esta es otra ventaja de los procesos independientes sin canal de comunicación fuera del paso de mensajes. Puedes hacer que funcionen de la misma manera ya sea que estén en una computadora local o en otra, lo que hace que la tolerancia a fallas a través de la distribución sea casi transparente para el programador.
El hecho de estar distribuido tiene consecuencias directas en cómo los procesos pueden comunicarse entre sí. Uno de los mayores obstáculos de la distribución es que no puedes asumir que, porque un nodo (una computadora remota) estaba allí cuando realizaste una llamada de función, seguirá estando allí durante toda la transmisión de la llamada o que incluso la ejecutará correctamente. Si alguien se tropieza con un cable o desenchufa la máquina, tu aplicación se quedará colgada. O tal vez se bloqueará. ¿Quién sabe?
Bueno, resulta que la elección del paso de mensajes asincrónico también fue una buena elección de diseño. En el modelo de procesos con mensajes asincrónicos, los mensajes se envían de un proceso a otro y se almacenan en un buzón dentro del proceso receptor hasta que se extraen para leerlos. Es importante mencionar que los mensajes se envían sin siquiera verificar si el proceso receptor existe o no, porque no sería útil hacerlo. Como se implica en el párrafo anterior, es imposible saber si un proceso se bloqueará entre el momento en que se envía y se recibe un mensaje. Y si se recibe, es imposible saber si se actuará en consecuencia o, nuevamente, si el proceso receptor morirá antes de eso. Los mensajes asincrónicos permiten llamadas de funciones remotas seguras porque no se supone qué sucederá; el programador es quien debe saberlo. Si necesita tener una confirmación de entrega, debe enviar un segundo mensaje como respuesta al proceso original. Este mensaje tendrá la misma semántica segura, y también la tendrá cualquier programa o biblioteca que cree sobre este principio.
Implementación: Bien, se decidió que los procesos livianos con paso de mensajes asincrónicos eran el enfoque a adoptar para Erlang. ¿Cómo hacer que esto funcione? En primer lugar, no se puede confiar en el sistema operativo para que se encargue de los procesos. Los sistemas operativos tienen muchas formas diferentes de gestionar los procesos y su rendimiento varía mucho. La mayoría, si no todos, son demasiado lentos o demasiado pesados para lo que necesitan las aplicaciones estándar de Erlang. Al hacer esto en la máquina virtual, los implementadores de Erlang mantienen el control de la optimización y la fiabilidad. Hoy en día, los procesos de Erlang ocupan unas 300 palabras de memoria cada uno y se pueden crear en cuestión de microsegundos, algo que no se puede hacer en los principales sistemas operativos de la actualidad.
Las colas de ejecución de Erlang en los núcleos: Para gestionar todos estos procesos potenciales que podrían crear sus programas, la máquina virtual inicia un hilo por núcleo que actúa como programador. Cada uno de estos programadores tiene una cola de ejecución, o una lista de procesos de Erlang en los que dedicar una parte del tiempo. Cuando uno de los programadores tiene demasiadas tareas en su cola de ejecución, algunas se migran a otra. Es decir, cada máquina virtual de Erlang se encarga de realizar todo el equilibrio de carga y el programador no tiene que preocuparse por ello. Se realizan otras optimizaciones, como limitar la velocidad a la que se pueden enviar mensajes en procesos sobrecargados para regular y distribuir la carga.
Todo el material pesado está ahí, administrado. Eso es lo que hace que sea fácil trabajar en paralelo con Erlang. Trabajar en paralelo significa que el programa debería funcionar el doble de rápido si agrega un segundo núcleo, cuatro veces más rápido si hay 4 más y así sucesivamente, ¿cierto? Depende. Este fenómeno se denomina escalamiento lineal en relación con la ganancia de velocidad en comparación con la cantidad de núcleos o procesadores. En la vida real, no existe nada gratis.
domingo, 5 de marzo de 2023
Primeros pasos con Phoenix
Antes de empezar de manera muy rápida aclaremos que Phoenix es un framework web o para hacer API en Elixir.
Phoenix está escrito en Elixir, y nuestro código de aplicación también estará escrito en Elixir. Por ende el primer paso es instalar Elixir , en mi caso lo voy a instalar con asdf :
asdf plugin-add elixir https://github.com/asdf-vm/asdf-elixir.git
asdf install elixir 1.14.3-otp-25
asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git
asdf install erlang 25.0.3
Y ahora vamos a setear las versiones:
asdf global erlang 25.0.3
asdf global elixir 1.14.3-otp-25
Pero pueden ver como instalar en su equipo en el siguiente link : https://elixir-lang.org/install.html
Cuando instalamos Elixir siguiendo las instrucciones de la página de instalación de Elixir, normalmente también obtendremos Erlang. Para checkear esto hacemos :
emanuel@crespo:~$ elixir --version
Erlang/OTP 25 [erts-13.1.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
Elixir 1.14.3 (compiled with Erlang/OTP 25)
Si acabamos de instalar Elixir por primera vez, también necesitaremos instalar el administrador de paquetes Hex. Hex es necesario para ejecutar una aplicación de Phoenix (mediante la instalación de dependencias) y para instalar cualquier dependencia adicional que podamos necesitar en el camino. En mi caso es :
mix local.hex
Dejo link; https://www.phoenixframework.org/
jueves, 1 de mayo de 2014
Manejar la concurrencia con Actores.
El modelo de actores que fue propuesto por primera vez por Carl Hewitt en 1973; es un modelo de concurrencia computacional que al igual que los hilos, trata de solucionar el problema de la concurrencia.
En el modelo de actores, cada objeto es un actor. Esta es una entidad que tiene una cola de mensajes o buzón y un comportamiento. Los mensajes pueden ser intercambiados entre los actores y se almacenan en el buzón. Al recibir un mensaje, el comportamiento del actor se ejecuta. El actor puede : enviar una serie de mensajes a otros actores, crear una serie de actores y asumir un nuevo comportamiento para el próximo mensaje.
La importancia en este modelo es que todas las comunicaciones se llevan a cabo de forma asincrónica. Esto implica que el remitente no espera a que un mensaje sea recibido en el momento que lo envío, solo sigue su ejecución.
Una segunda característica importante es que todas las comunicaciones se producen por medio de mensajes: no hay un estado compartido entre los actores. Si un actor desea obtener información sobre el estado interno de otro actor, se tendrá que utilizar mensajes para solicitar esta información. Esto permite a los actores controlar el acceso a su estado, evitando problemas.
Erlang es un lenguaje de programación concurrente y un sistema de ejecución que incluye una máquina virtual y bibliotecas. Fue diseñado en la compañía Ericsson para realizar aplicaciones distribuidas, tolerantes a fallos y de funcionamiento ininterrumpido. Originalmente, Erlang era un lenguaje propietario de Ericsson, pero fue cedido como software de código abierto en 1998. La implementación de Ericsson es, principalmente interpretada pero también incluye un compilador HiPE (sólo soportado en algunas plataformas).
Entre los mayores logros de la plataforma podemos destacar que el chat de facebook y la base documental CouchDB.
Sin dudas una cosa que hace muy especial de Erlang es como maneja la concurrencia. Erlang no maneja la concurrencia con hilos como nos tiene acostumbrado C, C++ o Java. Erlang soluciona la programación concurrente mediante el modelo de actores.
En Erlang los procesos o actores:
- Son rápidos de crear y destruir.
- El envío de mensajes entre procesos es muy rápido.
- Es fácil mantener un gran número de procesos.
- Son ligeros.
- Son independientes y no comparten memoria.
- Sólo un proceso tratará un mensaje pasado, en ningún caso pasará por otro proceso.
Parece complicado, pero no lo es. Con un ejemplo vamos a aclarar el tema. Supongamos que queremos hacer un actor saludador, pero no muy simpático; que salude solo a los conocidos; en erlang sería así:
-module(saludador).
-export([loop/0]).
loop() ->
receive
% Saluda a un conocido
"conocido" ->
io:format("Hola!! " ),
loop();
% Un desconocido, no lo saluda
_ ->
io:format(" ... " ),
loop()
end.
En la primera línea declaramos el modulo; luego importamos la función loop, con la cual definimos una función vacía e iteramos para siempre. Con receive recibimos un mensaje, es similar al swich, espera a recibir un mensaje y al recibir un mensaje ejecuta la estructura de código que corresponde al mensaje enviado y el “_” es como el default del swich en c, c++ o java.
Primero compilamos, para esto vamos a guardar nuestro saludador en el fichero saludador.erl y luego en la consola de Erlang escribimos:
1> c(saludador).
{ok,saludador}
Con spawn se puede generar un proceso, spawn nos devolverá el PID del proceso, que nos servirá para enviarle mensajes.
2> Pid = spawn(fun saludador:loop/0).
<0.38.0>
Ahora le vamos a mandar un mensaje:
4> Pid ! "conocido".
Hola!! "conocido"
5> Pid ! "Pepe".
... "Pepe"
Como era de esperar solo dice hola a los conocidos. Con el operador ! enviamos mensajes a un actor a partir de su Pid.
Éste es un pequeño ejemplo de manejo de concurrencia en erlang. La forma en que maneja la concurrencia Scala fue inspirada en Erlang.
Akka es un framework java que nos brinda este modelo en el lenguaje java. Permitiéndonos manejar la concurrencia de forma más simple como lo hace scala o erlang.
Veamos un ejemplo:
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.actor.ActorRef;
public class HelloWorld extends UntypedActor {
@Override
public void preStart() {
// create the greeter actor
final ActorRef greeter = getContext().actorOf(Props.create(Greeter.class), "greeter");
// tell it to perform the greeting
greeter.tell(Greeter.Msg.GREET, getSelf());
}
@Override
public void onReceive(Object msg) {
if (msg == Greeter.Msg.DONE) {
// when the greeter is done, stop this actor and with it the application
getContext().stop(getSelf());
} else
unhandled(msg);
}
}
Como podemos ver en ejemplo podemos hacer un manejo de actores de forma remota. Akka es tolerable a fallos dado que fue concedido con tecnología Let it crash/Embrace failure. Akka es de código abierto y está disponible bajo la licencia Apache 2. A la vez está basado en scala y provee una Api para este lenguaje.
Akka tiene integración con Spring, soporta OSGI, se integra con Apache Camel y la última versión soporta Java 8. ¡Solo falta que cocine y lave la ropa!
Desarrollar aplicaciones concurrentes puede ser un dolor de cabeza. Por este motivo nació el modelo de actores, para simplificar el desarrollo concurrente.
martes, 14 de mayo de 2024
Conversiones de tipos en Erlang
Erlang, como muchos lenguajes, cambia el tipo de un término al convertirlo en otro. Esto se hace con la ayuda de funciones integradas, ya que muchas de las conversiones no se pudieron implementar en Erlang. Cada una de estas funciones toma la forma <tipo>_to_<tipo> y se implementa en el módulo erlang. Éstos son algunos de ellos:
1> erlang:list_to_integer("54").
54
2> erlang:integer_to_list(54).
"54"
3> erlang:list_to_integer("54.32").
** exception error: bad argument
in function list_to_integer/1
called as list_to_integer("54.32")
4> erlang:list_to_float("54.32").
54.32
5> erlang:atom_to_list(true).
"true"
6> erlang:list_to_bitstring("hi there").
<<"hi there">>
7> erlang:bitstring_to_list(<<"hi there">>).
"hi there"
Etcétera. Estamos topando con un problema de lenguaje: debido a que se usa el esquema <tipo>_to_<tipo>, cada vez que se agrega un nuevo tipo al lenguaje, es necesario agregar una gran cantidad de BIF de conversión. Aquí está la lista:
atom_to_binary/2, atom_to_list/1, binary_to_atom/2, binary_to_existing_atom/2, binary_to_list/1, bitstring_to_list/1, binary_to_term/1, float_to_list/1, fun_to_list/1, integer_to_list/1, integer_to_list/2, iolist_to_binary/1, iolist_to_atom/1, list_to_atom/1, list_to_binary/1, list_to_bitstring/1, list_to_existing_atom/1, list_to_float/1, list_to_integer/2, list_to_pid/1, list_to_tuple/1, pid_to_list/1, port_to_list/1, ref_to_list/1, term_to_binary/1, term_to_binary/2 and tuple_to_list/1.
jueves, 22 de febrero de 2024
Listas en Erlang
Las listas se utilizan para resolver todo tipo de problemas y son sin duda la estructura de datos más utilizada en Erlang. ¡Las listas pueden contener cualquier cosa! Números, átomos, tuplas, otras listas. La notación básica de una lista es [Elemento1, Elemento2,..., ElementoN] y puedes mezclar más de un tipo de datos en ella:
> [1, 2, 3, {numbers,[4,5,6]}, 5.34, atom].
[1,2,3,{numbers,[4,5,6]},5.34,atom]
domingo, 26 de mayo de 2013
Continuamos con Erlang
Se acuerdan del post: http://emanuelpeg.blogspot.com.ar/2013/05/vistazo-erlang.html bueno vamos a seguir dándole un vistazo a Erlang.
Las variables en Erlang son un tanto diferentes que otros lenguajes; al ser un lenguaje funcional solo se le pueden asignar valores a varible solo una vez. Es decir que las variables no pueden variar :P
Si necesitamos asignar otro valor, necesitamos otra variable.
3> Integer = 4.
4
4> Integer = Integer + 5.
** exception error: no match of right hand side value 9
5>
viernes, 15 de septiembre de 2023
Y es Erlang tan bueno?
Actualmente, Erlang está ganando mucha popularidad debido a conversaciones entusiastas que pueden llevar a la gente a creer que es más de lo que realmente es. El primer caso de esto está relacionado con las enormes capacidades de escalamiento de Erlang debido a sus procesos livianos. Es cierto que los procesos de Erlang son muy ligeros: puedes tener cientos de miles de ellos existentes al mismo tiempo, pero esto no significa que tengas que usarlos de esa manera sólo porque puedas. Por ejemplo, crear un juego de disparos en el que todo, incluidas las balas, sea su propio actor, es una locura. Lo único que dispararás en un juego como este será tu propio pie. Todavía hay un pequeño costo al enviar un mensaje de actor a actor, y si divides demasiado las tareas, ¡harás las cosas más lentas!
También se dice que Erlang puede escalar de manera directamente proporcional a la cantidad de núcleos que tiene su computadora, pero esto generalmente no es cierto. Es posible, pero la mayoría de los problemas no se comportan. de una manera que te permita ejecutar todo al mismo tiempo.
Hay algo más a tener en cuenta: si bien Erlang hace algunas cosas muy bien, técnicamente todavía es posible obtener los mismos resultados en otros lenguajes. Lo opuesto también es cierto; evaluar cada problema como debe ser y elegir la herramienta adecuada según el problema que se aborda. Erlang no es una solución mágica y será particularmente malo en cosas como procesamiento de imágenes y señales, controladores de dispositivos de sistemas operativos, etc. y brillará en cosas como software grande para uso de servidor (es decir, colas, reducción de mapas), haciendo algo de levantamiento junto con otros lenguajes, implementación de protocolos de nivel superior, etc. Las áreas intermedias dependerán de usted. No necesariamente debes encerrarte en un software de servidor con Erlang: ha habido casos de personas que hacen cosas inesperadas y sorprendentes. Un ejemplo es IANO, un robot creado por el equipo UNICT, que utiliza Erlang por su inteligencia artificial y ganó la medalla de plata en el concurso eurobot de 2009. Otro ejemplo es Wings 3D, un modelador 3D de código abierto (pero no un renderizador) escrito en Erlang y, por lo tanto, multiplataforma.
Dejo link: https://learnyousomeerlang.com/introduction
domingo, 8 de mayo de 2011
Concurrencia en Erlang

Entre los mayores logros de la plataforma podemos destacar que el chat de facebook y la base documental CouchDB.
Sin dudas una cosa que hace muy especial a Erlang es como maneja la concurrencia. Erlang no maneja la concurrencia con thread como nos tiene acostumbrado C, C++ o Java. Erlang soluciona la programación concurente mediante el modelo de actores. El modelo de actores es un modelo de concurrencia computacional que trata a los "actores" como los primitivos universal de la computación digital en paralelo: en respuesta a un mensaje que recibe, un actor puede tomar decisiones locales, crear más actores, envía más mensajes, y determinar cómo responder al siguiente mensaje recibido.
Parece complicado, pero no lo es. Con un ejemplo vamos a aclarar el tema. Supongamos que queremos hacer un actor saludador, pero no muy simpático; que salude solo a los conocidos; en erlang sería así:
-module(saludador).
-export([loop/0]).
loop() ->
receive
% Saluda a un conocido
"conocido" ->
io:format("Hola!! " ),
loop();
% Un desconocido, no lo saluda
_ ->
io:format(" ... " ),
loop()
end.
En la primera linea declaramos el modulo; luego importamos la función loop, con la cual definimos una función vacía y iteramos para siempre. Con receive recibimos un mensaje, es similar al swich, espera a recibir un mensaje y al recibir un mensaje ejecuta la estructura de codigo que corresponde al mensaje enviado y el “_” es como el default en c, c++ o java.
Primero compilamos:
1> c(saludador).
{ok,saludador}
Con spawn se puede generar un proceso, spawn nos devolverá el PID del proceso, que nos servirá para enviarle mensajes.
2> Pid = spawn(fun saludador:loop/0).
<0.38.0>
Ahora le vamos a mandar un mensaje:
4> Pid ! "conocido".
Hola!! "conocido"
5> Pid ! "Pepe".
... "Pepe"
Como era de esperar solo dice hola a los conocidos. Con el operador ! enviamos mensajes a un actor a partir de su Pid.
Este es un pequeño ejemplo de manejo de concurrencia en erlang. La forma en que maneja la concurrencia Scala fue inspirada en Erlang.
domingo, 24 de marzo de 2024
Modulos en Erlang
Trabajar con el shell interactivo a menudo se considera una parte vital del uso de lenguajes de programación dinámicos. Es útil para probar todo tipo de códigos y programas. La mayoría de los tipos de datos básicos de Erlang se utilizaron sin necesidad de abrir un editor de texto o guardar archivos. Podrías dejar caer el teclado, salir a jugar y dar por terminado el día, pero serías un pésimo programador de Erlang si te detuvieras ahí mismo. ¡El código debe guardarse en algún lugar para poder usarse!
Para eso están los módulos. Los módulos son un conjunto de funciones reagrupadas en un único archivo, bajo un único nombre. Además, todas las funciones en Erlang deben definirse en módulos.
Los BIF del módulo erlang se diferencian de otras funciones en que se importan automáticamente cuando usa Erlang. Cualquier otra función definida en un módulo que vaya a utilizar debe llamarse con el formato Módulo:Función(Argumentos).
Puedes verlo por ti mismo:
1> erlang:element(2, {a,b,c}).
b
2> element(2, {a,b,c}).
b
3> lists:seq(1,4).
[1,2,3,4]
4> seq(1,4).
** exception error: undefined shell command seq/2
Aquí, la función seq del módulo de lista no se importó automáticamente, mientras que el element sí. El error 'exception error: undefined shell command seq/2' proviene de que el shell busca un comando de shell como f() y no puede encontrarlo. Hay algunas funciones del módulo erlang que no se importan automáticamente, pero no se usan con demasiada frecuencia.
Lógicamente, deberías poner funciones sobre cosas similares dentro de un solo módulo. Las operaciones comunes en listas se mantienen en el módulo de listas, mientras que las funciones para realizar entradas y salidas (como escribir en la terminal o en un archivo) se reagrupan en el módulo io. Uno de los únicos módulos que encontrará que no respeta ese patrón es el módulo erlang antes mencionado que tiene funciones que hacen matemáticas, conversiones, tratan con multiprocesamiento, modifican la configuración de la máquina virtual, etc. No tienen ningún punto en común excepto ser funciones integradas. Debes evitar la creación de módulos como erlang y, en su lugar, centrarte en separaciones lógicas limpias.
viernes, 9 de febrero de 2024
Álgebra booleana y operadores de comparación en Erlang
Uno estaría en serios problemas si no pudiera distinguir entre lo que es pequeño y lo grande, lo que es verdadero y lo falso. Como cualquier otro lenguaje, Erlang tiene formas que le permiten utilizar operaciones booleanas y comparar elementos.
El álgebra booleana es muy simple:
1> true and false.
false
2> false or true.
true
3> true xor false.
true
4> not false.
true
5> not (true and true).
false
Los operadores booleanos and y or siempre evaluarán los argumentos en ambos lados del operador. Si desea tener operadores de cortocircuito (que solo evaluarán el argumento del lado derecho si es necesario), use andalso y orelse.
La prueba de igualdad o desigualdad también es muy simple, pero tiene símbolos ligeramente diferentes a los que se ven en muchos otros lenguajes:
6> 5 =:= 5.
true
7> 1 =:= 0.
false
8> 1 =/= 0.
true
9> 5 =:= 5.0.
false
10> 5 == 5.0.
true
11> 5 /= 5.0.
false
En primer lugar, si su lenguaje habitual usa == y != para probar a favor y en contra de la igualdad, Erlang usa =:= y =/=. Las tres últimas expresiones (líneas 9 a 11) también nos presentan un problema: a Erlang no le importarán los números flotantes y enteros en aritmética, pero sí lo hará al compararlos. Pero no te preocupes, porque los operadores == y /= están ahí para ayudarte en estos casos. Es importante recordar esto si desea igualdad exacta o no.
Otros operadores para comparaciones son < (menor que), > (mayor que), >= (mayor o igual que) y =< (menor o igual que). Este último está al revés (en mi opinión) y es la fuente de muchos errores de sintaxis en mi código. Esté atento a eso =<.
12> 1 < 2.
true
13> 1 < 1.
false
14> 1 >= 1.
true
15> 1 =< 1.
true
¿Qué pasa al hacer 5 + llama o 5 == verdadero? ¡No hay mejor manera de saberlo que probarlo y luego asustarse con mensajes de error!
12> 5 + llama.
** exception error: bad argument in an arithmetic expression
in operator +/2 called as 5 + llama
¡Bien! ¡A Erlang realmente no le gusta que hagas mal uso de algunos de sus tipos fundamentales! El emulador devuelve un bonito mensaje de error aquí. ¡Nos dice que no le gusta uno de los dos argumentos utilizados en torno al operador +!
Sin embargo, algunas veces no se toma tan en serio el tema de los tipos :
13> 5 =:= true.
false
¿Por qué rechaza distintos tipos en unas operaciones pero no en otras? Si bien Erlang no te permite agregar nada con todo, te permitirá compararlos. Esto se debe a que los creadores de Erlang pensaron que el pragmaticismo vence a la teoría y decidieron que sería fantástico poder escribir simplemente cosas como algoritmos de clasificación generales que pudieran ordenar cualquier término. Está ahí para simplificarle la vida y puede hacerlo la gran mayoría del tiempo.
Hay una última cosa a tener en cuenta al hacer álgebra booleana y comparaciones:
14> 0 == false.
false
15> 1 < false.
true
Lo más probable es que te estés tirando de los pelos si vienes de lenguajes procedimentales o de la mayoría de los lenguajes orientados a objetos. ¡La línea 14 debe evaluarse como verdadera y la línea 15 como falsa! Después de todo, falso significa 0 y verdadero es cualquier otra cosa. Excepto en Erlang.
Erlang no tiene valores booleanos verdadero y falso. Los términos verdadero y falso son átomos, pero están lo suficientemente bien integrados en el lenguaje como para no tener problemas con eso, siempre y cuando no esperes que falso y verdadero signifiquen otra cosa que falso y verdadero.
Nota: El orden correcto de cada elemento en una comparación es el siguiente:
number < atom < reference < fun < port < pid < tuple < list < bit string
¡Solo recuerda que es por eso que puedes comparar cualquier cosa con cualquier cosa! Para citar a Joe Armstrong, uno de los creadores de Erlang: "El orden real no es importante, pero sí es importante que un orden total esté bien definido".
sábado, 3 de febrero de 2024
Erlang shell
En Erlang, puedes probar la mayoría de tus cosas en un emulador; ejecutará sus scripts cuando los compile e implemente, pero también te permite ejecutar código en vivo. Para esto, iniciamos el shell en Linux y luego escribimos $ erl. Y si esta todo bien, deberíamos ver un texto como este:
Erlang R13B01 (erts-5.7.2) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.7.2 (abort with ^G)
Para los usuarios de Windows, aún pueden ejecutar el shell erl.exe, pero se recomienda que se utilice werl.exe, que se puede encontrar en el menú de inicio (programas > Erlang). Werl es una implementación solo para Windows del shell Erlang, que tiene su propia ventana con barras de desplazamiento y admite edición de línea de comandos (como copiar y pegar, lo que llegó a ser una molestia con el shell cmd.exe estándar en Windows). El shell erl aún es necesario si desea redirigir la entrada o salida estándar, o utilizar canalizaciones.
Podremos ingresar y ejecutar código en el emulador, pero primero, veamos cómo podemos movernos en él. El shell Erlang tiene un editor de líneas incorporado basado en un subconjunto de Emacs, un popular editor de texto que se utiliza desde los años 70. Si conoce Emacs, debería estar bien. Para los demás, te irá bien de todos modos.
En primer lugar, si escribe algo de texto y luego va ^A (Ctrl+A), debería ver que el cursor se mueve al principio de la línea. ^E (Ctrl+E) te lleva al final. Puede usar las teclas de flecha para avanzar, retroceder, mostrar líneas anteriores o siguientes para poder repetir el código.
Si escribe algo como li y luego presiona "tab", el shell habrá completado los términos y si presionamos tabulador nuevamente y el shell le sugerirá muchas funciones para usar después. Este es Erlang completando las listas de módulos y luego sugiriendo funciones a partir de ellos.
Creo que hemos visto suficiente funcionalidad del shell para estar bien, excepto por una cosa: ¡no sabemos cómo salir! Hay una forma rápida de descubrir cómo hacerlo. Simplemente escriba help(). y debería obtener información sobre un montón de comandos que puede usar en el shell (no olvide el punto (.) ya que es necesario para que se ejecute el comando). Usaremos algunos de ellos más adelante, pero la única línea que nos preocupa para poder salir es
q() -- salir - abreviatura de init:stop()
Esta es una manera de hacerlo (de hecho, dos maneras). Si estabas prestando atención, cuando inició el shell, hubo un comentario sobre "abortar con ^G". ¡Hagámoslo y luego presione h para obtener ayuda!
User switch command
--> h
c [nn] - connect to job
i [nn] - interrupt job
k [nn] - kill job
j - list all jobs
s [shell] - start local shell
r [node [shell]] - start remote shell
q - quit erlang
? | h - this message
-->
Si escribe i y luego c, Erlang debería detener el código que se está ejecutando actualmente y devolverlo a un shell responsivo. j le dará una lista de procesos en ejecución (una estrella después de un número indica que este es el trabajo que está ejecutando actualmente), que luego puede interrumpir con i seguido del número. Si usa k, matará el shell tal como está en lugar de simplemente interrumpirlo. Presione s para iniciar uno nuevo.
Eshell V5.7.2 (abort with ^G)
1> "OH NO THIS SHELL IS UNRESPONSIVE!!! *hits ctrl+G*"
User switch command
--> k
--> c
Unknown job
--> s
--> j
2* {shell,start,[]}
--> c 2
Eshell V5.7.2 (abort with ^G)
1> "YESS!"
Si vuelve a leer el texto de ayuda, notará que podemos iniciar shells remotos.
lunes, 4 de marzo de 2024
Manipular datos binarios con Erlang
La mayoría de los lenguajes admiten la manipulación de datos como números, átomos, tuplas, listas, registros y/o estructuras, etc. La mayoría de ellos también solo tienen funciones muy básicas para manipular datos binarios. Erlang hace todo lo posible para proporcionar abstracciones útiles cuando se trata de valores binarios con coincidencia de patrones llevada al siguiente nivel. Hace que tratar con datos binarios sin procesar sea divertido y fácil, lo cual era necesario para las aplicaciones de telecomunicaciones para las que fue creado. La manipulación de bits tiene una sintaxis y modismos únicos que pueden parecer un poco extraños al principio, pero si sabes cómo funcionan generalmente los bits y los bytes, esto debería tener sentido.
La sintaxis de bits encierra datos binarios entre << y >>, los divide en segmentos legibles y cada segmento está separado por una coma. Un segmento es una secuencia de bits de un binario (no necesariamente en un límite de bytes, aunque este es el comportamiento predeterminado). Digamos que queremos almacenar un píxel naranja (24 bits). Si alguna vez comprobó los colores en Photoshop o en una hoja de estilos CSS para la web, sabrá que la notación hexadecimal tiene el formato #RRGGBB. Un tinte naranja es #F09A29 en esa notación, que podría ampliarse en Erlang a:
1> Color = 16#F09A29.
15768105
2> Pixel = <<Color:24>>.
<<240,154,41>>
Básicamente dice "Coloque los valores binarios de #F09A29 en 24 bits (rojo en 8 bits, verde en 8 bits y azul también en 8 bits) en la variable Píxel". Posteriormente se puede tomar el valor para escribirlo en un archivo. Esto no parece mucho, pero una vez escrito en un archivo, lo que obtendría al abrirlo en un editor de texto sería un montón de caracteres ilegibles. Cuando vuelva a leer el archivo, Erlang interpretará el binario en el bonito formato <<240,151,41>> nuevamente.
Lo que es más interesante es la capacidad de hacer coincidir patrones con archivos binarios para descomprimir contenido:
3> Pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
4> <<Pix1,Pix2,Pix3,Pix4>> = Pixels.
** exception error: no match of right hand side value <<213,45,132,64,76,32,76,
0,0,234,32,15>>
5> <<Pix1:24, Pix2:24, Pix3:24, Pix4:24>> = Pixels.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
Lo que hicimos en el comando 3 fue declarar lo que serían exactamente 4 píxeles de colores RGB en binario.
En la expresión 4, intentamos descomprimir 4 valores del contenido binario. Lanza una excepción, porque tenemos más de 4 segmentos, ¡de hecho tenemos 12! Entonces, lo que hacemos es decirle a Erlang que cada variable del lado izquierdo contendrá 24 bits de datos. Eso es lo que significa Var:24. Luego podemos tomar el primer píxel y descomprimirlo en valores de un solo color:
6> <<R:8, G:8, B:8>> = <<Pix1:24>>.
<<213,45,132>>
7> R.
213
"Sí, eso es genial. ¿Y si solo quisiera el primer color desde el principio? ¿Tendré que descomprimir todos estos valores todo el tiempo?" ¡Ja! ¡No lo dudes! Erlang introduce más azúcar sintáctico y coincidencia de patrones para ayudarte con:
8> <<R:8, Rest/binary>> = Pixels.
<<213,45,132,64,76,32,76,0,0,234,32,15>>
9> R.
213
Bonito, ¿eh? Esto se debe a que Erlang acepta más de una forma de describir un segmento binario. Todos estos son válidos:
Value
Value:Size
Value/TypeSpecifierList
Value:Size/TypeSpecifierList
donde Tamaño representará bits o bytes (según el Tipo y Unidad a continuación) y TypeSpecifierList representa uno o más de los siguientes:
integer | float | binary | bytes | bitstring | bits | utf8 | utf16 | utf32
Esto representa el tipo de datos binarios utilizados. Tenga en cuenta que "bytes" es la abreviatura de "binario" y "bits" es la abreviatura de "cadena de bits". Cuando no se especifica ningún tipo, Erlang asume un tipo "entero".
Firma
Valores posibles: signed | unsigned
Solo importa para la coincidencia cuando el tipo es un número entero. El valor predeterminado es "sin firmar".
Endianidad
Valores posibles: big | little | native
La endianidad solo importa cuando el tipo es entero, utf16, utf32 o flotante. Esto tiene que ver con cómo el sistema lee los datos binarios. Como ejemplo, el formato de encabezado de imagen BMP mantiene el tamaño de su archivo como un número entero almacenado en 4 bytes. Para un archivo que tiene un tamaño de 72 bytes, un sistema little-endian lo representaría como <<72,0,0,0>> y uno big-endian como <<0,0,0,72>>. Uno se leerá como '72' mientras que el otro se leerá como '1207959552', así que asegúrese de utilizar el endianismo correcto. También existe la opción de usar 'nativo', que elegirá en tiempo de ejecución si la CPU usa little endianness o big endianness de forma nativa. De forma predeterminada, la endianidad está establecida en "grande".
Unidad
unidad unit:Integer
Este es el tamaño de cada segmento, en bits. El rango permitido es 1..256 y está establecido de forma predeterminada en 1 para números enteros, flotantes y cadenas de bits y en 8 para binarios. Los tipos utf8, utf16 y utf32 no requieren que se defina ninguna unidad. La multiplicación de Tamaño por Unidad es igual a la cantidad de bits que tomará el segmento y debe ser divisible por 8. El tamaño de la unidad generalmente se usa para garantizar la alineación de bytes.
TypeSpecifierList se construye separando los atributos por un '-'.
Algunos ejemplos pueden ayudar a digerir las definiciones:
10> <<X1/unsigned>> = <<-44>>.
<<"Ô">>
11> X1.
212
12> <<X2/signed>> = <<-44>>.
<<"Ô">>
13> X2.
-44
14> <<X2/integer-signed-little>> = <<-44>>.
<<"Ô">>
15> X2.
-44
16> <<N:8/unit:1>> = <<72>>.
<<"H">>
17> N.
72
18> <<N/integer>> = <<72>>.
<<"H">>
19> <<Y:4/little-unit:8>> = <<72,0,0,0>>.
<<72,0,0,0>>
20> Y.
72
Puede ver que hay más de una forma de leer, almacenar e interpretar datos binarios. Esto es un poco confuso, pero aún así es mucho más sencillo que utilizar las herramientas habituales que ofrecen la mayoría de los lenguajes.
Las operaciones binarias estándar (desplazamiento de bits a izquierda y derecha, 'y' binario, 'o', 'xor' o 'no') también existen en Erlang. Simplemente use las funciones bsl (Bit Shift Left), bsr (Bit Shift Right), band, bor, bxor y bnot.
2#00100 = 2#00010 bsl 1.
2#00001 = 2#00010 bsr 1.
2#10101 = 2#10001 bor 2#00101.
sábado, 1 de febrero de 2025
Concurrencia en Erlang parte 10
Ahora volvamos a los links y procesos que mueren. La propagación de errores entre procesos se realiza a través de un proceso similar al paso de mensajes, pero con un tipo especial de mensaje llamado señales o signals. Las señales de salida son mensajes "secretos" que actúan automáticamente sobre los procesos, matándolos en la acción.
Ya he mencionado muchas veces que para ser confiable, una aplicación necesita poder matar y reiniciar un proceso rápidamente. En este momento, los enlaces están bien para hacer la parte de matar. Lo que falta es el reinicio.
Para reiniciar un proceso, necesitamos una forma de saber primero que murió. Esto se puede hacer agregando una capa sobre los enlaces con un concepto llamado procesos del sistema. Los procesos del sistema son básicamente procesos normales, excepto que pueden convertir señales de salida en mensajes normales. Esto se hace llamando a process_flag(trap_exit, true) en un proceso en ejecución. Nada dice tanto como un ejemplo, así que lo usaremos. Simplemente volveré a hacer el ejemplo de la cadena con un proceso del sistema al principio:
1> process_flag(trap_exit, true).
true
2> spawn_link(fun() -> linkmon:chain(3) end).
<0.49.0>
3> receive X -> X end.
{'EXIT',<0.49.0>,"chain dies here"}
Ahora las cosas se ponen interesantes. Volviendo a nuestros dibujos, lo que sucede ahora es más bien así:
[shell] == [3] == [2] == [1] == [0]
[shell] == [3] == [2] == [1] == *dead*
[shell] == [3] == [2] == *dead*
[shell] == [3] == *dead*
[shell] <-- {'EXIT,Pid,"chain dies here"} -- *dead*
[shell] <-- still alive!
Y este es el mecanismo que permite reiniciar rápidamente los procesos. Al escribir programas que utilizan procesos del sistema, es fácil crear un proceso cuyo único papel sea comprobar si algo muere y luego reiniciarlo cuando falle.
Por ahora, quiero volver a las funciones de excepción que vimos anteriormente y mostrar cómo se comportan en torno a los procesos que atrapan salidas. Primero, establezcamos las bases para experimentar sin un proceso del sistema. Mostraré sucesivamente los resultados de lanzamientos, errores y salidas no atrapados en procesos vecinos:
spawn_link(fun() -> ok end)
catch exit(normal)
, except a PID is added to the tuple to know what processed failed.spawn_link(fun() -> exit(reason) end)
spawn_link(fun() -> exit(normal) end)
spawn_link(fun() -> 1/0 end)
{badarith, Reason}
) is never caught by a try ... catch
block and bubbles up into an 'EXIT'. At this point, it behaves exactly the same as exit(reason)
did, but with a stack trace giving more details about what happened.spawn_link(fun() -> erlang:error(reason) end)
1/0
. That's normal, erlang:error/1
is meant to allow you to do just that.spawn_link(fun() -> throw(rocks) end)
throw
is never caught by a try ... catch
, it bubbles up into an error, which in turn bubbles up into an EXIT. Without trapping exit, the process fails. Otherwise it deals with it fine.Y eso es todo en lo que respecta a las excepciones habituales. Las cosas son normales: todo va bien. Suceden cosas excepcionales: los procesos mueren, se envían diferentes señales.
Luego está exit/2. Este es el equivalente a un arma en el proceso Erlang. Permite que un proceso mate a otro a distancia, de forma segura. Estas son algunas de las posibles llamadas:
- Exception source:
exit(self(), normal)
- Untrapped Result: ** exception exit: normal
- Trapped Result: {'EXIT', <0.31.0>, normal}
- When not trapping exits,
exit(self(), normal)
acts the same asexit(normal)
. Otherwise, you receive a message with the same format you would have had by listening to links from foreign processes dying. - Exception source:
exit(spawn_link(fun() -> timer:sleep(50000) end), normal)
- Untrapped Result: - nothing -
- Trapped Result: - nothing -
- This basically is a call to
exit(Pid, normal)
. This command doesn't do anything useful, because a process can not be remotely killed with the reasonnormal
as an argument. - Exception source:
exit(spawn_link(fun() -> timer:sleep(50000) end), reason)
- Untrapped Result: ** exception exit: reason
- Trapped Result: {'EXIT', <0.52.0>, reason}
- This is the foreign process terminating for reason itself. Looks the same as if the foreign process called
exit(reason)
on itself. - Exception source:
exit(spawn_link(fun() -> timer:sleep(50000) end), kill)
- Untrapped Result: ** exception exit: killed
- Trapped Result: {'EXIT', <0.58.0>, killed}
- Surprisingly, the message gets changed from the dying process to the spawner. The spawner now receives
killed
instead ofkill
. That's becausekill
is a special exit signal. More details on this later. - Exception source:
exit(self(), kill)
- Untrapped Result: ** exception exit: killed
- Trapped Result: ** exception exit: killed
- Oops, look at that. It seems like this one is actually impossible to trap. Let's check something.
- Exception source:
spawn_link(fun() -> exit(kill) end)
- Untrapped Result: ** exception exit: killed
- Trapped Result: {'EXIT', <0.67.0>, kill}
- Now that's getting confusing. When another process kills itself with
exit(kill)
and we don't trap exits, our own process dies with the reasonkilled
. However, when we trap exits, things don't happen that way.
Aunque se pueden atrapar la mayoría de las razones de salida, hay situaciones en las que se puede querer asesinar brutalmente un proceso: tal vez uno de ellos esté atrapando salidas pero también está atascado en un bucle infinito, sin leer ningún mensaje. La razón de eliminación actúa como una señal especial que no se puede atrapar. Esto garantiza que cualquier proceso que se termine con ella realmente estará muerto. Por lo general, matar es un último recurso, cuando todo lo demás ha fallado.
Como la razón de eliminación nunca se puede atrapar, se debe cambiar a matar cuando otros procesos reciben el mensaje. Si no se cambiara de esa manera, todos los demás procesos vinculados a ella morirían a su vez por la misma razón de eliminación y, a su vez, matarían a sus vecinos, y así sucesivamente. Se produciría una cascada de muertes.
Esto también explica por qué exit(kill) parece muerto cuando se recibe de otro proceso vinculado (la señal se modifica para que no se produzca una cascada), pero sigue pareciendo muerto cuando se atrapa localmente.
Si todo esto le parece confuso, no se preocupe. Muchos programadores sienten lo mismo. Las señales de salida son un poco curiosas. Afortunadamente, no hay muchos más casos especiales que los descritos anteriormente. Una vez que los comprenda, podrá comprender la mayor parte de la gestión de errores concurrentes de Erlang sin problemas.