Confusion over the use of the pin (^) operator

Viewed 252

I'm looking at some Elixir code that looks like this:

  @to_preload [
    :last_modified_by,
    change_request: [application: [:app_definition]],
    comments: [:user]
  ]

  def preload(query) do
    preload(query, ^@to_preload)
  end

I believe the call preload(query, ^@to_preload) is actually to the Query.preload/3 macro as the file has an import Ecto.Query.

So my question is, what exactly is the ^ doing in front of the module attribute @to_preload in that call?

1 Answers

You're right. preload there is referring to Ecto.Query.preload/3. From Ecto's docs:

When writing a query, you are inside Ecto's query syntax. In order to access params values or invoke Elixir functions, you need to use the ^ operator, which is overloaded by Ecto

This is why the pin operator (^) is being used. You need to use it so that the module attribute can be interpolated.

If you were to write the query without using the pin operator, you would get the following error:

** (Ecto.Query.CompileError) `@to_preload` is not a valid preload expression.
preload expects an atom, a list of atoms or a keyword list with more preloads as
values. Use ^ on the outermost preload to interpolate a value

So, the code you shared would be equivalent to:

def preload(query) do
  preload(query, [
    :last_modified_by,
    change_request: [application: [:app_definition]],
    comments: [:user]
  ])
end

There's actually an example in preload's documentation using the pin operator. It could be confusing that the function that is being defined is called preload as well. However, because of the arity, there's no confusion, since Ecto.Query's preload could take 2 or 3 arguments, whereas the one defined here only takes 1.

Related