me: have you ever done the ‘make a process that runs a function that does a receive and calls that function again after receiving every message’ thing yet?
person1: that was a sentence that I have no idea what it means
me: I had already done erlang for like 3 months when I got into elixir, and kind of dug in hard on erlang, so I had these concepts in the brain already
me: ok, let me show you real quick
me: this is like the thing about elixir
me: in elixir, processes are what you should use for a ton of stuff. Like everything nearly. And when you do, life is amazing.
me: one sec, I’ll make a repo for you
person1: I’m pretty much a pure web dev, we do stateless things
person1: none of this ‘wait for messages and do things’ stuff
me: http://github.com/knewter/process_actor_example
person1: looking
me: if either of you spent any decent time (more than a couple of weeks) learning elixir and weren’t introduced to that, it’s a sad state of affairs 😞
me: this is my 12th episode of elixirsips, so I guess that wasn’t within 2 weeks
me: but still
person1: yeah that kind of stuff was covered in Programming Elixir
me: this is like….the fundamental thing in elixir/erlang
me: ok
me: so some issues with that. If the process dies (however) it’s gone for good. So you can’t rely on it being there. So you can’t build things that rely on it. So you won’t get a super reliable system using that as your primitive without some extra bits on top of it
me: the first is spawn_link
me: if you use that instead of spawn, then you both create a new process and link it to the current process (the shell, in the case of using iex)
me: you could do that without spawn_link
, too
me: http://elixir-lang.org/docs/stable/elixir/Process.html#link/1
me: and if you just wanted to receive a message when that process died, but not have it kill you too, that can be done with monitor
s
me: ok, so if you use those things you can actually build reliable systems - if you want that ping service running all the time, you could spawn a simple process early on that started it, monitored it, and started a new one if/when it died
me: that’s how early erlang systems were built, and it was Good
me: and then they realized they did this all the freaking time, both of those patterns
me: and that there was no good way to get debugging information on those running processes so it was problematic in production to make the system better or understand what was happening
me: so they introduced GenServer
to provide a common way to do things with the first pattern
me: it has GenServer.call(pid, msg)
and GenServer.cast(pid, msg)
. The first is synchronous, so you can use it like a ruby method call or w/e. The second is async, so you can send a message to another process and get on with life no matter what else happens.
me: everything in the messaging layer in erlang is asynchronous - it’s built on this concept of each process having a mailbox
that you put messages in. receive
looks in the mailbox to try to pattern match some case. If there is a message that isn’t matched in the case, it crashes. If there is, it will handle that message and then return (done forever, with one receive). If there isn’t, it will block the process until it receives a message, handle it, and be done forever.
me: if you want to always receive messages, you can tail-call the function that receives them. typically you’d call this loop
but that’s just kind of a random convention no one cares about.
me: you can use this pattern to build up state
which is how you get ‘changing data’ in an immutable language
me: you take the previous state, do something based on the message, and call back into the loop with the ‘updated’ state (which is just a new immutable value based on the previous one)