I’m learning Elixir and I just got to the part about closures. When a language has closures, the first thing I usually do is try to make the closure algorithm. In JavaScript it looks something like this:
let counter = function() {
let count = 0;
return function() {
count += 1;
return count;
};
}();
Then, each time that counter
is called, it will return a new number in sequence.
counter(); // returns 1
counter(); // returns 2
counter(); // returns 3
counter(); // etc.
Is it possible to make this in Elixir? The main problem seems to be that count
would be immutable in Elixir. I could make it a one-element list, but that sounds like a Bad Idea™. What would be the Elixir way of dealing with this purely hypothetical situation?
2
Answers
In elixir, values are immutable, but variables can me made to point to different places in memory where other values are stored. For example, when the line
count = 2
executes, 2 is stored in memory somewhere, thencount
is bound to that new memory location. After that, no variables are bound to the memory location of 1, so that memory is ready for garbage collection.Elixir has closures because a function does carry with it the bindings in the environment in which the function was defined, but the bindings are to specific memory locations, and the values at those memory locations are immutable:
In iex:
…and it wouldn’t work. Lists are an example of how immutable values in memory are advantageous. When you add a value to the head of a list, elixir creates a whole new list somewhere else in memory. But, instead of copying the old list to the memory location of the new list, elixir knows that the old list is guaranteed to be immutable, so elixir can just use a pointer to the old list. The new memory location consists of the new elements of the list plus a pointer to the old list–no copying required. In the case of a closure, the binding would be to the location of the original list in memory, and any new lists created from the original list would reside elsewhere in memory.
In elixir/erlang, you can use something called a GenServer to preserve state between function calls:
When you write:
elixir looks for a callback function named
handle_call()
whose first argument matches:increment
and executes it, passing in the state as the third argument. You definehandle_call()
to do what you want, then you send a reply back to the calling process and set the new state.In iex:
If you need state, use a process. In Elixir, the idiomatic ways to do that are with Agent and GenServer.
The example from the Agent docs does exactly as you asked:
Usage: