Phoenix Web Development
上QQ阅读APP看书,第一时间看更新

Immutability in Elixir

The first thing to note is Elixir's concept of immutability. This means that any variables used will retain their value whenever they're passed along. This is a big difference from languages such as Ruby, where the state of any data you pass on is no longer guaranteed to retain its original value. Objects are a good example of this in Ruby.

To demonstrate this concept a little bit better (don't worry about the syntax yet; we'll get to that!), let's take a look at a code snippet in Javascript, as follows:

function upcase_name(p) {
p.name = p.name.toUpperCase();
}

var person = { name: "Brandon" } // Name here is still 'Brandon'
person // Output is { name: "Brandon" }
upcase_name(person)
person // Oh no! Output is now { name: "BRANDON" }

Oh no! We've just introduced a scenario where calling a function is destructive whether we've intended it to be or not! Let's look at a similar block of code in Elixir in the following example:

upcase = fn p ->
Map.put(p, :name, String.upcase(p.name))
end

person = %{ name: "Brandon" } # Name here is still 'Brandon'
upcase.(person)
person # Output for name is still 'Brandon'!

This is likely our intended behavior, and it prevents people from doing wacky things while programming, such as writing functions that end in? (typically used to denote a function that answers a question about the arguments in a Boolean). (Sadly, I've seen this more times than I'd care to admit).

Of course, none of these examples are without their own respective gotchas (and honestly, which programming language nowadays doesn't have its fair share?). For example, the following snippet is considered completely legitimate code in Elixir:

person = %{ name: "Brandon" }
person = %{ name: "Richey" }

But, wait! I hear you cry, I thought Elixir's variables are immutable!Well, the answer to that is that they are immutable! Or rather, the memory locations that they point to are; therefore, every time we create a variable we're essentially creating a pointer to a location in memory. In our case, it stores our previous map into some memory location (we'll call it 0 x 01). When we reassign a person, however, we store the value of %{ name: "Richey" } into a different memory location (we'll call it 0 x 02). This is what is being passed along to our functions when we pass in the person variable; here, we're basically telling Elixir, Hey, use the value stored in memory location 0 x 01, which is what the variable person is currently pointing at. This means that we don't have to worry about the value in 0 x 01 being changed as long as it is still in use.