Difference between `%Struct{} = struct` and `struct = %Struct{}` in defs

I noticed my function definitions had instances of def my_fun(struct = %Struct{field: field}) instead of the other way around (def my_fun(%Struct{field: field} = struct)), which seems more common.

My question is whether the order matters and why & how this works :slight_smile:

2 Likes

In this case order does not matter, the entire thing becomes a, an Erlang parlance, MatchSpec, it just makes sure the whole thing is matched out and returns a set of bindings to some of the internal data. :slight_smile:

2 Likes

Hmmmmm I see, thanks

2 Likes

There is no difference. Basically the = defines an alias where both sides are patterns which must match. In this case both the struct and the variable must match. It is typically used for maps, structs and records where you want to test the contents and get a reference to the whole but it is legal anywhere in a pattern. For example try this in the shell

iex(4)> [:a = x | [_] = y] = [:a,:b]
[:a, :b]
iex(5)> y
[:b]
iex(6)> x
:a
4 Likes

It’s first worth mentioning that a return value of a simple pattern match expression (pat = expression) is the value of the right-hand side expression:

iex> {_, _} = {:ok, 42}
{:ok, 42}

Pattern matches can be chained so pat1 = pat2 = term is equivalent to pat1 = (pat2 = term), and since the result of a match expression is the term itself, you’re just consecutively matching from right to left. The matching order can easily be proved in iex:

iex> ^a = a = 42
42

iex> b = ^b = 42
** (CompileError) iex:2: unbound variable ^b

The second match can’t work because chaining is right-associative, so ^b = 42 is done before b is bound and the compiler complains.

When it comes to your example, the order doesn’t matter. In fact, in most cases I’ve used/seen in practice I don’t think the order mattered. When chaining matches in function heads, or e.g. case clauses, I use the %Struct{...} = struct style for a couple of reasons:

  1. IIRC, I’ve seen this in first Erlang books and other resources I read when I was learning.
  2. As you said, it generally seems to be a prevalent convention.
  3. That style puts more emphasis on what I’m trying to branch on (%Struct{...}), which I think is a slightly better assistance for readers.
4 Likes

Thanks guys, I just didn’t understand how it relates to Kernel.SpecialForms.= (where the order does matter) but I think I understand now:

iex> x = 1
1
iex> 2 = y
** (CompileError) ...
```

Both match from right to left, as @sasajuric showed so the second example causes an error.

```elixir
def my_fun(pat1 = pat2) do
  # ...
end
```

Here the order doesn't matter cause when evaluated the complete expression becomes this (or so I think):

```elixir
pat1 = pat2 = value
```

So in any case (function invocation or not), the pattern matches from right to left. When the function is actually invoked the full expression becomes `pat1 = pat2 = value` so it happens to not make a difference and it's only a matter of taste / conventions.

:grin:
2 Likes

Precisely, when doing a binding expression (the iex> 2 = y in your example) the order matters like @sasajuric stated, but when doing a matchspec (the def my_fun(pat1 = pat2) do example) it just defines matchers, like @rvirding stated. :slight_smile:

You can actually craft matchspec’s directly and use them for various purposes, especially with mnesia lookups (I wish we had functionality in Ecto to use them). :slight_smile:

2 Likes