{"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 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 What happens if you introduce The code would work just as fine and the compiler would even warn if the newly added The semantics may have potentially changed if you wanted Let’s check Erlang. Given the code:<\/p>\n What happens if you introduce 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 What happens if we introduce it after its definition?<\/p>\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 Let’s say you want to match on a new value inside a case. In Elixir you would write:<\/p>\n What would happen if you accidentally introduce a Nothing, the code works just fine due to rebinding.<\/p>\n Let’s see what happens in Erlang:<\/p>\n And what happens when you introduce a variable?<\/p>\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 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 Because Elixir explicitly matches, if you remove the definition of If you remove the At this point, Elixir:<\/p>\n while Erlang:<\/p>\n At the beginning, we have mentioned someone may introduce a new variable In Erlang:<\/p>\n Now what happens if we want to introduce a new version of The code just works. What about Erlang?<\/p>\n If the developer introduces a new variable and forgets to change Some will say that a benefit of numbered variables is that further code could use any of However I would consider the code above to be a poor practice because there is nothing in the name Because both Elixir and Erlang variables provide implicit behaviour, rebinding and pattern matching respectively, both require care when adding or removing variables to existing code. Therefore, if Elixir can be source of hidden bugs, we have shown Erlang is the source of similar bugs under different situations. Not only that, Erlang requires both previous and further knowledge of the context when introducing new variables while Elixir requires only further knowledge. The only way to circumvent those bugs in both languages is by either forbidding or explicitly providing both rebinding and pattern match<\/em> operations, which none of the languages do.<\/p>\n It is possible some will react to this article by saying: “this does not happen in my code”. The truth is that it does happen, even in small functions:<\/p>\n On the other hand, it does not mean writing code in Erlang or Elixir is going to lead to more bugs in your software. After all, Erlang developers have been writing robust software for decades. Those “quirks” exist in any language and they end-up internalized by programmers as they get experienced. That’s exactly from where the “this does not happen in my code” comes from.<\/p>\n At the end of the day, no language will guarantee you can safely change code without caring about its context. There will always be “hidden bugs”. For example, in languages like Clojure, JavaScript and Ruby, variables and function names exist in the same namespace, so introducing variables may change the semantics of function calls. Since both Erlang and Elixir provide two namespaces, one for variables and another for functions, they are shielded from these particular “hidden bugs”.<\/p>\n Furthermore, type systems, compiler warnings, test suites are all techniques that help solve those problems. Languages may also provide patterns, like the Elixir pipe operator ( At least, I hope this puts to rest the claim that Elixir variables are somehow unsafer than Erlang ones (or vice-versa).<\/p>\n Thanks to Joe Armstrong, Sa\u0161a Juric, James Fish, Chris McCord, Bryan Hunter, Sean Cribbs and Anthony Ramine for reviewing this article and providing feedback.<\/em><\/p>\n 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. Before we start, a short disclaimer: Elixir does not have mutable variables, it has rebinding. Mutability is often associated … \u00bb<\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[1],"tags":[143],"aioseo_notices":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/4956"}],"collection":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/comments?post=4956"}],"version-history":[{"count":19,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/4956\/revisions"}],"predecessor-version":[{"id":4992,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/posts\/4956\/revisions\/4992"}],"wp:attachment":[{"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/media?parent=4956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/categories?post=4956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.plataformatec.com.br\/wp-json\/wp\/v2\/tags?post=4956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}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
foo_bar = ...\n# some code\nuse_foo_bar(foo_bar)\n<\/code><\/pre>\n
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
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
use_foo_bar<\/code> to use the first
foo_bar<\/code> variable. Indeed, careless change may cause bugs.<\/p>\n
FooBar = ...\n% some code\nuse_foo_bar(FooBar)\n<\/code><\/pre>\n
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
FooBar<\/code> will no longer be assigned to but matched on.<\/p>\n
FooBar = ...\n% some code\nFooBar = ... % newly added line and it errors\nuse_foo_bar(FooBar)\n<\/code><\/pre>\n
Case<\/h2>\n
case some_expr() do\n {:ok, safe_value} -> perform_something_safe()\n _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\n
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
case some_expr() of\n {ok, SafeValue} -> perform_something_safe();\n _ -> perform_something_unsafe()\nend\n<\/code><\/pre>\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
SafeValue<\/code> but it will match against it.<\/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
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
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
\n
^<\/code> for explicit match<\/li>\n<\/ul>\n
\n
Numbered variables<\/h2>\n
foo_bar<\/code> in the Elixir code and change the code semantics if the variable was already used later on. However, most of those cases are desired. For example, in Elixir:<\/p>\n
foo_bar = step1()\nfoo_bar = step2(foo_bar)\nfoo_bar = step3(foo_bar)\n# some code\nuse_foo_bar(foo_bar)\n<\/code><\/pre>\n
FooBar0 = step1(),\nFooBar1 = step2(FooBar0),\nFooBar2 = step3(FooBar1),\n% some code\nuse_foo_bar(FooBar2)\n<\/code><\/pre>\n
foo_bar<\/code> (
step_4<\/code>) in Elixir?<\/p>\n
foo_bar = step1()\nfoo_bar = step2(foo_bar)\nfoo_bar = step3(foo_bar)\nfoo_bar = step4(foo_bar) # newly added line\n# some code\nuse_foo_bar(foo_bar)\n<\/code><\/pre>\n
FooBar0 = step1(),\nFooBar1 = step2(FooBar0),\nFooBar2 = step3(FooBar1),\nFooBar3 = step4(FooBar2),\n% some code\nuse_foo_bar(FooBar2) % All FooBar2 must be changed\n<\/code><\/pre>\n
FooBar2<\/code> later on, the code semantics changed, introducing the same bug rebinding in Elixir would. This is particularly troubling if you change all but miss one variable, since the code won’t emit “unused variable” warnings. This is even more prone to errors when adding an intermediate step (say between
step2<\/code> and
step3<\/code>).<\/p>\n
FooBar2<\/code> and
FooBar3<\/code>, for example:<\/p>\n
FooBar0 = step1(),\nFooBar1 = step2(FooBar0),\nFooBar2 = step3(FooBar1),\nFooBar3 = step4(FooBar2),\n% some code\nuse_foo_bar(FooBar2),\nsomething_else(FooBar3)\n<\/code><\/pre>\n
FooBar2<\/code> that hints to why it is different than
FooBar3<\/code>. In this case, the variable names would not reflect at all why part of the code would prefer to use one over the other. Your team will be much better off by giving explicit names instead of versioned ones.<\/p>\n
Summing up<\/h2>\n
\n
|><\/code>), to help to convert repetitive code into more readable and less error-prone versions.<\/p>\n
\n
\n<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"