Why are pin operators necessary in Ecto queries?

2020-02-23 06:11发布

问题:

In Elixir, the pin operator is used to prevent variable rebinding. However, with regard to an Ecto query like

from u in User, where: u.username == ^username

the authors of Programming Phoenix state (in chapter 7) that

Remember, the ^ operator (called the pin operator) means we want to keep ^username the same.

But this doesn't sound right, because apparently, the comparison in the query shall not cause any rebinding of variables.

Are the authors of the book (which José Valim co-authored) mistaken? Is the pin operator in Ecto queries merely a construct of the Ecto DSL instead of a usual Elixir pin operator? Or will the query really get a chance to rebind username after the macros are expanded?

回答1:

Ecto's queries rely on macros to provide the powerful DSL that we use. That means that, whatever comes after from, is not "regular" Elixir code, but a DSL which will eventually get transformed to SQL query. So, a caret there is not pin operator per se, and has nothing to do with pattern matching (although obviously it still can be called "pin operator" because people always forget words such as caret and ampersand and asterisk). It's just a convenient operator that Ecto's authors choose to be an "interpolation operator". Without it, the username from your example would be taken literally, and inserted directly to the generated SQL (though Ecto is smart enough to see that that's not what you want so it spills an error).

Great question BTW, inspired me to read more about macros (newbie in FP here).



回答2:

According to Ecto documentation, the pin operator in ecto is used for query interpolation:

External values and Elixir expressions can be injected into a query expression with ^

def with_minimum(age, height_ft) do
  from u in "users",
    where: u.age > ^age and u.height > ^(height_ft * 3.28),
    select: u.name
end

Trying to skip the pin will give you an error as Ecto cann't figure out a database function or query expression named age:

(Ecto.Query.CompileError) variable age is not a valid query expression. Variables need to be explicitly interpolated in queries with ^



标签: elixir ecto