User |> where(id: ^user_id) を実行しようとすると、 Ecto.Query.where
で内部で以下のような実装になっていました。
defmacro where(query, binding \\ [], expr) do
Filter.build(:where, query, binding, expr, __CALLER__)
end
expr
に [id: ^user_id]
が入っています。
Ecto.Query.Builder.Filter.build/5
def build(kind, query, binding, expr, env) do
binding = Builder.escape_binding(binding)
{expr, params} = escape(kind, expr, binding, env)
...
exprがescapeされているようです。
Ecto.Query.Builder.Filter.escape/4
def escape(kind, expr, vars, env) when is_list(expr) do
{parts, params} =
Enum.map_reduce(expr, %{}, fn
{field, nil}, _acc ->
Builder.error! "nil given for #{inspect field}. Comparison with nil is forbidden as it is unsafe. " <>
"Instead write a query with is_nil/1, for example: is_nil(s.#{field})"
{field, value}, acc when is_atom(field) ->
{value, params} = Builder.escape(value, {0, field}, acc, vars, env)
...
field
に id
、 value
に ^user_id
が入ります。
# param interpolation
def escape({:^, _, [arg]}, type, params, _vars, _env) do
index = Map.size(params)
params = Map.put(params, index, {arg, type})
expr = {:{}, [], [:^, [], [index]]}
{expr, params}
end
^user_id
を {:^, _, [arg]},
として受け取っています。
この表記は内部表現と呼ばれるもので、コンソール上で以下のように確認できました。
iex(19)> quote do: ^user_id
{:^, [], [{:user_id, [], Elixir}]}
iex(20)> quote do: ^(1 + 2)
{:^, [], [{:+, [context: Elixir, import: Kernel], [1, 2]}]}
これにより、^
がピン演算子として評価されていそうです。