En Elixir, todo el código se ejecuta dentro de procesos.
De forma predeterminada, una función se ejecuta en el mismo proceso desde el que se la ha llamado. Cuando necesites ejecutar explícitamente una función en un proceso nuevo, usa spawn/1:
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/1 crea un proceso nuevo que ejecuta la función de aridad 0 que se le pasa y devuelve un identificador de proceso (PID). El proceso nuevo permanecerá vivo mientras se ejecute la función y, después, terminará de forma silenciosa.
No hay que confundir los procesos de Elixir con los procesos del sistema operativo. Los procesos de Elixir consumen mucha menos memoria y CPU. No hay ningún problema en tener aplicaciones de Elixir que ejecutan cientos de procesos de Elixir.
Los procesos no comparten información directamente entre sí. Los procesos envían y reciben mensajes para compartir datos.
Puedes enviar un mensaje a cualquier proceso con send/2. El primer argumento de send/2 es el PID del destinatario; el segundo argumento es el mensaje.
Un mensaje puede ser de cualquier tipo. A menudo consiste en átomos y tuplas. Si quieres recibir una respuesta, debes incluir el PID del emisor en algún lugar del mensaje. Puedes obtener el PID del proceso actual con self().
send/2 no comprueba si el destinatario ha recibido el mensaje ni si el destinatario sigue vivo. El mensaje acaba en el buzón del destinatario y solo se leerá si y cuando el destinatario pida explícitamente recibir mensajes.
Se puede leer un mensaje de un buzón con la macro receive/1. Acepta un bloque do que puede hacer coincidencia de patrones con los mensajes.
receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
:do_nothing -> nil
end
receive/1 tomará un mensaje del buzón que coincida con cualquiera de los patrones dados y ejecutará la expresión correspondiente a ese patrón. Si no hay mensajes en el buzón, o ninguno de los mensajes del buzón coincide con alguno de los patrones, receive/1 esperará a recibir uno.
Si quieres recibir más de un mensaje, debes llamar a receive/1 de forma recursiva. Es un patrón habitual implementar una función recursiva, llamada por ejemplo loop, que llama a receive/1, hace algo con el mensaje y luego se llama a sí misma para esperar más mensajes. Si necesitas conservar algún estado de una llamada a receive/1 a otra, puedes hacerlo pasando un argumento a esa función loop.
Los identificadores de proceso son un tipo de dato propio. Funcionan como direcciones de buzón: si tienes el PID de un proceso, puedes enviarle un mensaje a ese proceso. Por lo general, los PID se crean de forma indirecta, como valor devuelto de funciones que crean procesos nuevos, como spawn.
Estás escribiendo un sistema embebido para una máquina de repartir números. Es un modelo muy sencillo. Puede repartir números consecutivos e informar de cuál fue el último número repartido.
Implementa la función start/0. Debe crear un proceso nuevo y devolver el PID del proceso. El proceso nuevo todavía no necesita hacer nada.
TakeANumber.start()
# => #PID<0.138.0>
Ten en cuenta que cada vez que ejecutes este código, el PID puede ser diferente.
Modifica la máquina para que el proceso recién creado esté listo para recibir mensajes (inicia un bucle de recepción) con un estado inicial de 0. Debe poder recibir mensajes {:report_state, sender_pid}. Como respuesta a esos mensajes, debe enviar su estado actual (el número del último ticket repartido) a sender_pid y después esperar más mensajes.
machine_pid = TakeANumber.start()
# a client sending a message to the machine
send(machine_pid, {:report_state, self()})
# a client receiving a message from the machine
receive do
msg -> msg
end
# => 0
Modifica la máquina para que pueda recibir mensajes {:take_a_number, sender_pid}. Debe aumentar su estado en 1, enviar el nuevo estado a sender_pid y después esperar más mensajes.
machine_pid = TakeANumber.start()
# a client sending a message to the machine
send(machine_pid, {:take_a_number, self()})
# a client receiving a message from the machine
receive do
msg -> msg
end
# => 1
Modifica la máquina para que pueda recibir un mensaje :stop. Debe dejar de esperar más mensajes.
Modifica la máquina para que, cuando reciba un mensaje inesperado, lo ignore y siga esperando más mensajes.
Regístrate en Exercism para aprender y dominar Elixir con 58 conceptos168 ejercicios y mentoría humana real, todo gratis.