Zprávy mají s funkcemi společnou jedinou věc, a to, že synchronně vracejí odpověď. Tím jejich podobnost končí.
To je minimálně zavádějící tvrzení.
1. funkce jsou statické, zprávy jsou akce
Pokud se "funkcí" myslí to, co funkce znamená v (čistém) FP, tak funkce není nic jiného než zkratka pro nějaký dlouhý výraz - kdekoliv dám funkci, tam bych mohl dát ten dlouhý výraz (referenční transparentnost), nic víc. Celý program v čistém FP je vlastně striktně vzato sémanticky úplně statický - není to popis
činnosti (co se má udělat kdy a jak), je to úplně statický popis
relací mezi daty. Např. fce sqr(x) neříká "vem x a udělej s ním něco", říká, že čísla 3 a 9 jsou v relaci sqr. Protože samozřejmě chceme programovat nějakou činnost, má FP jazyk nějaký runtime, který tu statickou strukturu vezme a
podle ní něco dělá. V samotném jazyce ale nic "dělat" nejde. Není jak.
2. zprávy nemusí být synchronní
...a dokonce je lepší, když defaultně nejsou, protože sesynchronizovat asynchronní události je v dobře navrženém jazyce triviální (kód v jazyce Elixir):
# synchronni fce - vrati :ok nebo :timeout
def ping_sync do
# poslu zpravu agentovi
send(agent,{self(),:ping})
# cekam na odpoved nebo timeout
receive do
:pong -> :ok
after 5000 -> :timeout
end
end
3. odeslání zprávy nemusí nic "vracet" nebo může "vracet" víc hodnot postupně
Např. můžu actoru poslat zprávu
{self(),:subscribe_seconds}
a on mi od té doby bude každou sekundu posílat zprávy typu
{:seconds_now,1485285333}
Zprávy pochopitelně chodí úplně nezávisle na tom, co zrovna příjemce dělá (tj. musí tam být nějaký mailbox). A je čistě na příjemci, jestli je zahodí, nebo si hodnotu aktuálního času napíše na čelo lihovou fixkou
...prostě asynchronní zprávy kombinované se vzájemně neblokovanými aktory/agenty mají obrovské možnosti, které se dost těžko představují, dokud si v tom člověk fakt prakticky nezkusí něco napsat.
Jak se modelují změny stavů pomocí immutable, jsem se stále nedozvěděl, ale to neznamená, že to nejde, třeba jo.
Immutable je immutable, takže pokud si pod "změnou stavu" představuješ změnu in situ, tak nijak. Jinak to ale může vypadat třeba takhle (jeden příklad z jiného světa, když odpověď "monády" ti nestačila):
# zjednodušený ilustrační příklad, v reálu by to vypadalo trochu jinak
defmodule StateHolder do
# tahle funkce spustí agenta držícího stav
def start_link() do
# spustí funkci loop s parametry [0] v novém vlákně
spawn_link(fn -> loop(0) end)
end
def loop(state) do
receive do
{:add,k} ->
new_state = state + k
IO.puts "stav se meni z #{state} na #{new_state}"
loop(new_state)
{:get_state,pid} ->
send(pid,{:state_is,state})
loop(state)
end
end
end