1. 21

The author of this is José Valim. I’m having a hard time understanding his argument and posting it here hoping that someone is able to provide some clarity.

  1. 7

    The argument is that the slight differences between the Elixir & Erlang syntaxes lead to fewer bugs:

    1) case expressions rebind their match variables. It just doesn’t work that way in Erlang: you’ve got to be sure you’re matching an unbound variable. In Elixir, it doesn’t matter if the variable is bound or not, it will rebind (as if it were unbound in the first place). If you want to match against its value, you write ^variable = ..., which doesn’t have a separate syntax in Erlang.

    foo = 25
    case {:bar, 7} do
      {:bar, foo} -> "#{foo} is seven"

    That will create two separate foos; they’d be one and the same in Erlang. If you did want to match on the value of foo:

    foo = 25
    case {:bar, 25} do
      {_, ^foo} -> "#{foo} is twenty-five, because I ^pinned it"
    case {:bar, 13} do
      {_, foo} -> "#{foo} can be anything"

    So instead of overloading how variables are matched in Erlang (bound? match its value; unbound? always match & bind), Elixir separates the two (^foo matches its value; foo will always match & bind).

    2) Elixir will rebind existing variables without complaining (foo = 3; foo = 5) and will force all later mentions of foo to use the last-bound value (5). This is basically the automatic version of rebinding variables by hand in Erlang, appending a ‘version number’ (so to speak) to the end.

    I think Elixir goes a step further by making it easy to write pipelines, which cut down on the number of variables you need. Instead of inserting an extra variable in the middle of a series of computations, you just insert a function:

    Foo = 3,
    Foo2 = 2 * Foo


    Foo = 3,
    Foo2 = math.sqrt(Foo)
    Foo3 = 2 * Foo2 % careful you change this to Foo2! and all code below to use Foo3!

    But instead you could write (in Elixir) foo = 3 |> (&(2*&1)).() and foo = 3 |> :math.sqrt |> (&(2*&1)).()

    Neither language is fool-proof. Elixir is arguably better than Erlang. Fewer sharp, pointy edges. Let me know if anything’s unclear!

    1. 4

      I have to agree, that was a confusing explanation for a simple difference. I personally like Erlang’s minimalism, where:

      Example = "one". % binding variable
      Example = "one". % pattern match variable (true)

      The runtime errors are usually sufficient to catch these mistakes in most situations.

      I would like to see the implications of Elixir’s auto-rebinding in a broader scope rather than the narrow example of case statements. While I know those are used heavily in Erlang programs, I’m curious about the risks introduced by rebinding in a larger more complex application with lots of moving parts.

      This is likely something that would require using Elixir for a period of time, which I haven’t yet tried, rather than being easy to communicate through blog post code samples.

      Regardless, in those cases I would prefer the stricter immutability of variables that Erlang offers. Especially considering the programming style of using multiple processes and the use of concurrency in even basic programs.

      1. 2

        I would personally prefer it if Elixir had Erlang’s matching by default, but optional rebinding with ^ rather than what they’re doing right now.

      2. 4

        Another way of looking at this is to recognize that the syntax for patterns is ambiguous. Is

        {3, Foo}

        a pattern match on tuples of values 3 and Foo or tuples of first value 3 and of any second value which is henceforth bound as Foo? There’s no a priori reason to prefer either interpretation and each has it’s own complication.

        If you choose the first method then you need a new syntax for explicitly calling out how wildcards induce binding. If you choose the second you need a new way to express equality constraints (often, guards, but also Elixir’s caret syntax).