In Elixir, tutto il codice viene eseguito all'interno di processi.
Per impostazione predefinita, una funzione viene eseguita nello stesso processo da cui è stata chiamata. Quando devi eseguire esplicitamente una certa funzione in un nuovo processo, usa spawn/1:
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/1 crea un nuovo processo che esegue la funzione a zero argomenti indicata e restituisce un identificatore di processo (PID). Il nuovo processo rimane attivo fintanto che la funzione viene eseguita, poi termina silenziosamente.
I processi di Elixir non vanno confusi con i processi del sistema operativo. I processi di Elixir usano molta meno memoria e CPU. Va benissimo avere applicazioni Elixir che eseguono centinaia di processi Elixir.
I processi non condividono direttamente le informazioni tra loro. I processi inviano e ricevono messaggi per condividere dati.
Puoi inviare un messaggio a qualsiasi processo con send/2. Il primo argomento di send/2 è il PID del destinatario, il secondo è il messaggio.
Un messaggio può essere di qualsiasi tipo. Spesso è composto da atomi e tuple. Se vuoi ricevere una risposta, dovresti includere da qualche parte nel messaggio il PID del mittente. Puoi ottenere il PID del processo corrente con self().
send/2 non controlla se il messaggio è stato ricevuto dal destinatario, né se il destinatario è ancora attivo. Il messaggio finisce nella casella di posta del destinatario e viene letto solo se e quando il destinatario chiede esplicitamente di ricevere messaggi.
Un messaggio può essere letto da una casella di posta usando la macro receive/1. Accetta un blocco do che può eseguire il pattern matching sui messaggi.
receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
:do_nothing -> nil
end
receive/1 prende un solo messaggio dalla casella di posta che corrisponde a uno qualsiasi dei pattern indicati ed esegue l'espressione indicata per quel pattern. Se nella casella di posta non ci sono messaggi, o nessuno dei messaggi corrisponde a uno dei pattern, receive/1 aspetta che ne arrivi uno.
Se vuoi ricevere più di un messaggio, devi chiamare receive/1 in modo ricorsivo. È uno schema comune implementare una funzione ricorsiva, ad esempio chiamata loop, che chiama receive/1, fa qualcosa con il messaggio e poi chiama se stessa per attendere altri messaggi. Se devi portare con te uno stato da una chiamata a receive/1 a un'altra, puoi farlo passando un argomento a quella funzione loop.
Gli identificatori di processo sono un tipo di dato a sé. Fungono da indirizzi della casella di posta: se hai il PID di un processo, puoi inviargli un messaggio. I PID vengono di solito creati indirettamente, come valore restituito dalle funzioni che creano nuovi processi, come spawn.
Stai scrivendo un sistema embedded per una macchina che distribuisce numeri. È un modello molto semplice. Può distribuire numeri consecutivi e riportare qual è stato l'ultimo numero distribuito.
Implementa la funzione start/0. Deve avviare un nuovo processo e restituire il PID del processo. Il nuovo processo non deve ancora fare nulla.
TakeANumber.start()
# => #PID<0.138.0>
Nota che ogni volta che esegui questo codice, il PID può essere diverso.
Modifica la macchina in modo che il processo appena avviato sia pronto a ricevere messaggi (avvia un ciclo di ricezione) con uno stato iniziale di 0. Deve essere in grado di ricevere messaggi {:report_state, sender_pid}. In risposta a questi messaggi, deve inviare il suo stato attuale (il numero dell'ultimo biglietto distribuito) a sender_pid e poi aspettare altri messaggi.
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 macchina in modo che possa ricevere messaggi {:take_a_number, sender_pid}. Deve aumentare il suo stato di 1, inviare il nuovo stato a sender_pid e poi aspettare altri messaggi.
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 macchina in modo che possa ricevere un messaggio :stop. Deve smettere di aspettare altri messaggi.
Modifica la macchina in modo che, quando riceve un messaggio inaspettato, lo ignori e continui ad aspettare altri messaggi.
Iscriviti a Exercism per imparare e padroneggiare Elixir con 58 concetti168 esercizi e il mentoring di persone reali, tutto gratis.