{"id":4956,"date":"2016-01-12T14:25:37","date_gmt":"2016-01-12T16:25:37","guid":{"rendered":"http:\/\/blog.plataformatec.com.br\/?p=4956"},"modified":"2016-01-13T08:01:58","modified_gmt":"2016-01-13T10:01:58","slug":"comparing-elixir-and-erlang-variables","status":"publish","type":"post","link":"https:\/\/blog.plataformatec.com.br\/2016\/01\/comparing-elixir-and-erlang-variables\/","title":{"rendered":"Comparing Elixir and Erlang variables"},"content":{"rendered":"

Sometimes Erlang programmers are worried “Elixir variables may be the source of hidden bugs”. This article discusses those concerns and shows how variables in Erlang can produce related “hidden bugs”, some of those eliminated by Elixir.<\/p>\n

Before we start, a short disclaimer: Elixir does not have mutable variables, it has rebinding. Mutability is often associated with storage. In Elixir, the values being stored cannot be changed (same as in Erlang). For an example of mutable variables, we could look at F#. In F# explicitly using the mutable<\/code> keyword (e.g. let mutable x = 5<\/code>) would allow us to change a value inside an inner loop (equivalent to a list comprehension or inside Enum.map<\/code>) and observe the change after the loop is over. That is mutability, and that is not possible in Elixir or Erlang without using explicit storage like processes or ETS.<\/p>\n

Back on track. This article will explore the potential for hidden bugs when changing code. Those bugs exist because both Erlang and Elixir variables provide implicit behaviour<\/em>. Elixir rebinds implicitly, Erlang pattern matches implicitly. Such bugs may show up if developers add or remove variables without being mindful of its context.<\/p>\n

Let’s see some examples. Imagine the following Elixir code:<\/p>\n

foo_bar = ...\n# some code\nuse_foo_bar(foo_bar)\n<\/code><\/pre>\n

What happens if you introduce foo_bar<\/code> before the snippet above?<\/p>\n

foo_bar = ... # newly added line\nfoo_bar = ...\n# some code\nuse_foo_bar(foo_bar)\n<\/code><\/pre>\n

The code would work just as fine and the compiler would even warn if the newly added foo_bar<\/code> is unused. What would happen, however, if the new line is introduced after the foo_bar<\/code> definition?<\/p>\n

foo_bar = ...\n# some code\nfoo_bar = ... # newly added line\nuse_foo_bar(foo_bar)\n<\/code><\/pre>\n

The semantics may have potentially changed if you wanted use_foo_bar<\/code> to use the first foo_bar<\/code> variable. Indeed, careless change may cause bugs.<\/p>\n

Let’s check Erlang. Given the code:<\/p>\n

FooBar = ...\n% some code\nuse_foo_bar(FooBar)\n<\/code><\/pre>\n

What happens if you introduce FooBar<\/code> before its definition?<\/p>\n

FooBar = ... % newly added line\nFooBar = ... % old line errors\n% some code\nuse_foo_bar(FooBar)\n<\/code><\/pre>\n

The Erlang code crashes at runtime instead of silently continuing. Certainly an improvement, but it still means that introducing a variable in Erlang requires us to certify the variable is not matched later on, as FooBar<\/code> will no longer be assigned to but matched on.<\/p>\n

What happens if we introduce it after its definition?<\/p>\n

FooBar = ...\n% some code\nFooBar = ... % newly added line and it errors\nuse_foo_bar(FooBar)\n<\/code><\/pre>\n

This time, the new line crashes. In other words, due to implicit matching in Erlang, we not only need to worry about all the code after<\/em> introducing a variable, but we also need to be mindful of all the code before<\/em> introducing it, as any previous code can cause future variables to become implicit matches.<\/p>\n

In other words, so far Elixir requires you to be mindful of all later code after the introduction of a variable while Erlang requires you to know all previous and further code before the introduction of a variable. The one benefit of Erlang so far is that the code may crash explicitly on the match.<\/p>\n

However, things get more complicated when considering case expressions.<\/p>\n

Case<\/h2>\n

Let’s say you want to match on a new value inside a case. In Elixir you would write:<\/p>\n

case some_expr() do\n  {:ok, safe_value} -> perform_something_safe()\n  _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\n

What would happen if you accidentally introduce a safe_value<\/code> variable in Elixir before that case statement?<\/p>\n

safe_value = ... # newly added line\n# some code\ncase some_expr() do\n  {:ok, safe_value} -> perform_something_safe()\n  _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\n

Nothing, the code works just fine due to rebinding.<\/p>\n

Let’s see what happens in Erlang:<\/p>\n

case some_expr() of\n  {ok, SafeValue} -> perform_something_safe();\n  _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\n

And what happens when you introduce a variable?<\/p>\n

SafeValue = ... % newly added line\n% some code\ncase some_expr() of\n  {ok, SafeValue} -> perform_something_safe();\n  _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\n

You have just silently introduced a potentially dangerous bug in your code! Again, because Erlang implicitly matches<\/em>, we may now accidentaly perform an unsafe operation as the first clause no longer binds to SafeValue<\/code> but it will match against it.<\/p>\n

Similar bug happens in Erlang when you are matching on an existing variable and you remove it. Imagine you have this working Elixir code:<\/p>\n

safe_value = ...\n# some code\ncase some_expr() do\n  {:ok, ^safe_value} -> perform_something_safe()\n  _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\n

Because Elixir explicitly matches, if you remove the definition of safe_value<\/code>, the code won’t even compile. Let’s see the working version of the Erlang one:<\/p>\n

SafeValue = ...\n% some code\ncase some_expr() of\n  {ok, SafeValue} -> perform_something_safe();\n  _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\n

If you remove the SafeValue<\/code> variable, the first clause will now bind to SafeValue<\/code> instead of matching, silently changing the behaviour of the code once again! Again, another bug while the Elixir approach has shielded us on both cases.<\/p>\n

At this point, Elixir:<\/p>\n