У Elixir весь код виконується всередині процесів.
За замовчуванням функція виконується в тому самому процесі, з якого її викликали. Коли потрібно явно запустити певну функцію в новому процесі, скористаймося spawn/1:
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/1 створює новий процес, який виконує передану функцію без аргументів і повертає ідентифікатор процесу (PID). Новий процес буде живим, доки виконується функція, а потім тихо завершиться.
Процеси Elixir не варто плутати з процесами операційної системи. Процеси Elixir використовують набагато менше памʼяті та процесорного часу. Цілком нормально мати застосунки Elixir, у яких працюють сотні процесів Elixir.
Процеси не діляться інформацією безпосередньо один з одним. Щоб обмінюватися даними, процеси надсилають і отримують повідомлення.
Можна надіслати повідомлення будь-якому процесу за допомогою send/2. Перший аргумент send/2 - це PID одержувача, а другий аргумент - саме повідомлення.
Повідомлення може бути будь-якого типу. Часто воно складається з атомів і кортежів. Якщо потрібно отримати відповідь, варто включити PID відправника кудись у повідомлення. PID поточного процесу можна отримати за допомогою self().
send/2 не перевіряє, чи повідомлення було отримано одержувачем, ані чи одержувач ще живий. Повідомлення потрапляє до поштової скриньки одержувача, і його буде прочитано лише тоді, коли одержувач явно попросить отримати повідомлення.
Повідомлення можна прочитати з поштової скриньки за допомогою макроса receive/1. Він приймає блок do, який уміє зіставляти повідомлення зі зразком.
receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
:do_nothing -> nil
end
receive/1 візьме одне повідомлення з поштової скриньки, яке відповідає будь-якому із заданих зразків, і виконає вираз, заданий для цього зразка. Якщо в поштовій скриньці немає повідомлень або жодне з повідомлень не відповідає жодному зі зразків, receive/1 чекатиме на одне з них.
Якщо потрібно отримати більше ніж одне повідомлення, потрібно викликати receive/1 рекурсивно. Поширений прийом - реалізувати рекурсивну функцію, наприклад, з назвою loop, яка викликає receive/1, щось робить із повідомленням, а потім викликає саму себе, щоб чекати на нові повідомлення. Якщо потрібно передати якийсь стан від одного виклику receive/1 до іншого, це можна зробити, передавши аргумент у цю функцію loop.
Ідентифікатори процесів - це окремий тип даних. Вони виконують роль адрес поштових скриньок: якщо у нас є PID процесу, ми можемо надіслати повідомлення цьому процесу. PID зазвичай створюються опосередковано, як повернене значення функцій, що створюють нові процеси, як-от spawn.
Ми пишемо вбудовану систему для машини, що видає номери. Це дуже проста модель. Вона може видавати послідовні номери й повідомляти, який номер було видано останнім.
Реалізуйте функцію start/0. Вона повинна створити новий процес і повернути PID цього процесу. Новий процес поки що не повинен нічого робити.
TakeANumber.start()
# => #PID<0.138.0>
Зауважте, що щоразу, коли ми запускаємо цей код, PID може бути іншим.
Змініть машину так, щоб новостворений процес був готовий отримувати повідомлення (запустивши цикл отримання) з початковим станом 0. Він повинен уміти отримувати повідомлення {:report_state, sender_pid}. У відповідь на ці повідомлення він повинен надіслати свій поточний стан (номер останнього виданого талона) до sender_pid, а потім чекати на нові повідомлення.
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
Змініть машину так, щоб вона могла отримувати повідомлення {:take_a_number, sender_pid}. Вона повинна збільшити свій стан на 1, надіслати новий стан до sender_pid, а потім чекати на нові повідомлення.
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
Змініть машину так, щоб вона могла отримувати повідомлення :stop. Вона повинна перестати чекати на нові повідомлення.
Змініть машину так, щоб, коли вона отримує неочікуване повідомлення, вона його ігнорувала й продовжувала чекати на нові повідомлення.
Зареєструйтеся на Exercism, щоб вивчати й опановувати Elixir, а також 58 концепцій168 вправ та справжнє наставництво від людей, і все це безкоштовно.