Search code examples
elixirecto

Confusion over the use of the pin (^) operator


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?


Solution

  • 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.