Understanding the Difference Between Pattern Matching and Binding in Function Signatures

Hey everyone,

I’m currently learning Elixir and I’m trying to understand the difference between two examples I came across. Here are the examples in question:

Example 1:

def create_video(%Accounts.User{} = user, attrs \\ %{}) do %Video{} |> Video.changeset(attrs) |> Ecto.Changeset.put_assoc(:user, user) |> Repo.insert() end

Example 2:

def create_video(user = %Accounts.User{}, attrs \\ %{}) do %Video{} |> Video.changeset(attrs) |> Ecto.Changeset.put_assoc(:user, user) |> Repo.insert() end

I know that outside of the function signature, pattern matching and variable binding have different meanings. But I’m not sure if there’s any functional difference between these two examples in terms of the code that’s executed. My understanding is that create_videowill only be called if a struct of type Accounts.Useris passed as an argument in both examples.

I’m currently reading the “Programming Phoenix” book, where I came across Example 1. However, I also saw Example 2 in the “Programming Elixir” book by Pragmatic Programmers. Can anyone help me understand the practical differences between these two examples? Is there a preference which style to use in this context?

Thanks in advance for your help!

1 Like

Hi @sorax94 welcome!

Short answer: There is no functional difference between these two examples. By convention however, most people do it %Accounts.User{} = user.

To elaborate, the order never matters in what is called a “match context”. These are some examples of a match context:

  1. A function head def create_video(%Accounts.User{} = user)
  2. A case clause
  3. A receive clause.

Within these clauses, the order doesn’t matter, and you can also add things like when guard clauses to the end. For example:

case result do
  {:ok, %Account{} = account} ->
end

is identical to

case result do
  {:ok, account = %Account{}} ->
end

The only case that’s a bit tricky is a regular old = when it’s sitting by itself in a function. When you do something like:

x = 1

This = is establishing a match context on its left hand side, and matching the contents of the right hand side against it. Importantly, this whole left hand side is a match context, so the things we said earlier are true. For example, these two things are equivalent:

{:ok, account = %Account{}} = result
{:ok, %Account{} = account} = result

Basically the = on the far right hand side there is establishing a match context on its left hand side, and so just like with case we can do the inner bit in any more. This distinction is a bit subtle and in that way a bit confusing, but in practice I think it ends up being pretty straight forward.

As with the function head note from before, the convention is to do it pattern first, so {:ok, %Account{} = account} = result. This is because it more closely matches how it would look if you did it in two simpler matches:

{:ok, account} = result
%Account{} = account
10 Likes

There’s a Credo check that you can set to allow only one or the other, but it defaults to not enforcing either.

Personally, I avoid the user = %Accounts.User{} form because my brain reads that as “user has a default value of an empty User struct” since that’s how Ruby syntax works inside a function’s argument list.

3 Likes

Also learning here.
The following added some more understanding for me:
Functions · Elixir School about half way down, it says “Remember that Elixir will pattern match the argument as it comes in [to the function’s argument]” and then further down … " If we switch the order of %{name: person_name} and person in the list, we will get the same result because each are matching to fred on their own."

1 Like

Thanks for the response! Yeah I could imagine that

Thanks for the detailed reply! Didn’t expect such a detailed reply!

This is an awesome and and incredibly nuanced answer… but Elixir is still a very very “simple” language. Stick with it and these “nuances” eventually will all make initiative sense! :+1:

2 Likes