Tutorial: Lightweight Processes
Due to its Erlang foundation, an LFE program is composed of anywhere from 1 to hundreds of thousands of lightweight processes. In fact, a 2005 message to comp.lang.functional reported spawning 20 million messages in a ring benchmark (on a 1.5 GHz SPARC with 16 GB RAM).
This is possible in part because each process operates independently with its own private memory, communicating via message passing, and the overhead for an Erlang process is pretty low (~1.5k for 32-bit and ~2.7k for 64-bit; see the Efficiency Guide's section on processes for more info).
This tutorial aims to decrease the mystery around Erlang processes and how to use them in LFE programs.
1 Interacting with Processes
1.1 Dump and flush
Processes in LFE are built from functions. These running functions communicate with other running functions via messages. When you start up the LFE REPL, you're using an Erlang process, and you can communicate with it just like any other process.
Let's get the REPL's process id and then send messages to the REPL using the PID:
The messages are sitting in the inbox of the process they were sent to, the REPL. If we flush the REPL's inbox, we can see them:
1.2 Getting Classy with receive
As you might imagine, there's a better way to do this. Let's send another message to the REPL's message queue (inbox):
Now let's take a look at that message without flushing the queue:
If there is a message in the inbox matching the pattern we have defined (in this case, a list of length 3), then we will have access to the data that is matched and bound to the variables. For more information on pattern matching, see the tutorial.
If there are a bunch of messages in the inbox, they will all be iterated over until a match is found:
Let's confirm that only the last message we entered was matched:
1.3 Shell spawn
So far, we've only look at the process for the REPL itself. We'd like to expand our horizons and look at creating a process in the REPL, writing to it instead of our shell.
However, we are faced with some difficulties:
* LFE doesn't let us define functions (or macros) in the REPL, and
* Erlang's spawn
function takes a module and function as a parameter.
We can sort of get around that first point using lambda
:
Let's update this function so that it can respond to messages when it's running
as an Erlang process using the call to receive
:
Now that we've got our message-capable function, let's spawn
it and
capture the process id so that we can write to it:
As you can see, when we send our message to the process we started with
spawn
, the process' function prints out the message that it received from
us.
We had to go through some gymnastics here, due to the limitations of the shell
and using funcall
in a spawn
call.
Up next: in an anti-intuitive twist, you'll see that doing the same thing from a module is more clear that doing it in the shell ;-)