Em Elixir, todo o código corre dentro de processos.
Por predefinição, uma função é executada no mesmo processo a partir do qual foi chamada. Quando precisas de executar explicitamente uma determinada função num novo processo, usa spawn/1:
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/1 cria um novo processo que executa a função de aridade 0 fornecida e devolve um identificador de processo (PID). O novo processo mantém-se vivo enquanto a função estiver a executar e, depois, termina silenciosamente.
Os processos de Elixir não devem ser confundidos com processos do sistema operativo. Os processos de Elixir usam muito menos memória e CPU. Não há problema nenhum em ter aplicações Elixir que correm centenas de processos Elixir.
Os processos não partilham informação diretamente entre si. Os processos enviam e recebem mensagens para partilhar dados.
Podes enviar uma mensagem a qualquer processo com send/2. O primeiro argumento de send/2 é o PID do destinatário; o segundo argumento é a mensagem.
Uma mensagem pode ser de qualquer tipo. Muitas vezes, é composta por átomos e tuplas. Se quiseres obter uma resposta, deves incluir o PID do remetente algures na mensagem. Podes obter o PID do processo atual com self().
send/2 não verifica se a mensagem foi recebida pelo destinatário nem se o destinatário ainda está vivo. A mensagem acaba na caixa de correio do destinatário e só é lida se e quando o destinatário pedir explicitamente para receber mensagens.
Uma mensagem pode ser lida de uma caixa de correio com a macro receive/1. Aceita um bloco do que pode fazer correspondência de padrões com as mensagens.
receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
:do_nothing -> nil
end
receive/1 retira uma mensagem da caixa de correio que corresponda a qualquer dos padrões indicados e executa a expressão indicada para esse padrão. Se não houver mensagens na caixa de correio, ou se nenhuma das mensagens na caixa de correio corresponder a algum dos padrões, receive/1 fica à espera de uma.
Se quiseres receber mais do que uma mensagem, tens de chamar receive/1 recursivamente. É um padrão comum implementar uma função recursiva, por exemplo com o nome loop, que chama receive/1, faz algo com a mensagem e depois se chama a si própria para esperar por mais mensagens. Se precisares de transportar algum estado de uma chamada a receive/1 para outra, podes fazê-lo passando um argumento a essa função loop.
Os identificadores de processo são um tipo de dados próprio. Funcionam como endereços de caixa de correio: se tiveres o PID de um processo, podes enviar uma mensagem a esse processo. Os PIDs são normalmente criados indiretamente, como valor devolvido de funções que criam novos processos, como spawn.
Estás a escrever um sistema embebido para uma máquina de senhas. É um modelo muito simples. Consegue emitir números consecutivos e indicar qual foi o último número emitido.
Implementa a função start/0. Deve criar um novo processo e devolver o PID desse processo. O novo processo ainda não precisa de fazer nada.
TakeANumber.start()
# => #PID<0.138.0>
Repara que, cada vez que executas este código, o PID pode ser diferente.
Altera a máquina para que o processo recém-criado esteja pronto para receber mensagens (inicia um ciclo de receção) com um estado inicial de 0. Deve conseguir receber mensagens {:report_state, sender_pid}. Como resposta a essas mensagens, deve enviar o seu estado atual (o número da última senha emitida) a sender_pid e depois aguardar mais mensagens.
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
Altera a máquina para que consiga receber mensagens {:take_a_number, sender_pid}. Deve aumentar o seu estado em 1, enviar o novo estado a sender_pid e depois aguardar mais mensagens.
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
Altera a máquina para que consiga receber uma mensagem :stop. Deve deixar de aguardar mais mensagens.
Altera a máquina para que, quando recebe uma mensagem inesperada, a ignore e continue a aguardar mais mensagens.
Inscreve-te no Exercism para aprenderes e dominares Elixir com 58 conceitos168 exercícios, e mentoria humana real, tudo grátis.