今回はマクロをサラッと説明するだけ
ロジックを生成する高位ロジックを作る
簡単に言うとプログラムを作るプログラム
ruby
{cat: 'Meow', dog: 'Bowow'}.each do |name, message|
define_method(name) do
puts message
end
end
メリット
マクロのある言語
など
((3 + 5) * 2) + (6 - 3)
elixir
x = 1
y = 2
x + y
{:__block__, [],
[
{:=, [], [
{:x, [], Elixir},
1
]},
{:=, [], [
{:y, [], Elixir},
2
]},
{:+, [context: Elixir, import: Kernel], [
{:x, [], Elixir},
{:y, [], Elixir}
]}
]
}
Q. このコードにはいくつのマクロが使われている?
defmodule Maybe do
def fmap(value, func) do
if is_nil(value) do
nil
else
func.(value)
end
end
end
A. 4つ
キーワードクエリシンタックス
LINQっぽい書き方でクエリを構築できる
1番めの投稿の全てのコメント
code
from c in Comment,
join: p in assoc(c, :post),
where: p.id == 1,
select: c
ast = quote do ~~~~ end
Macro.expand(ast, __ENV__)
AST
{:%{}, [],
[__struct__: Ecto.Query, assocs: [], distinct: nil,
from: {{{:., [], [Comment, :__schema__]}, [], [:source]}, Comment},
group_bys: [], havings: [],
joins: [{:%, [],
[{:__aliases__, [alias: Ecto.Query.JoinExpr, counter: -576460752303423045],
[:JoinExpr]},
{:%{}, [],
[qual: :inner, source: nil,
on: {:%, [],
[{:__aliases__, [alias: false, counter: -576460752303423045],
[:Ecto, :Query, :QueryExpr]},
{:%{}, [], [expr: true, params: [], line: 0, file: "iex"]}]},
assoc: {0, :post}, file: "iex", line: 0, params: []]}]}], limit: nil,
lock: nil, offset: nil, order_bys: [],
prefix: {{:., [], [Comment, :__schema__]}, [], [:prefix]}, preloads: [],
select: {:%, [],
[{:__aliases__, [alias: false, counter: -576460752303423029],
[:Ecto, :Query, :SelectExpr]},
{:%{}, [],
[expr: {:{}, [], [:&, [], [0]]}, params: [], file: "iex", line: 0,
take: {:%{}, [], []}]}]}, sources: nil, updates: [],
wheres: [{:%, [],
[{:__aliases__, [alias: false, counter: -576460752303423037],
[:Ecto, :Query, :QueryExpr]},
{:%{}, [],
[expr: {:{}, [],
[:==, [],
[{:{}, [],
[{:{}, [], [:., [], [{:{}, [], [:&, [], [1]]}, :id]]}, [], []]},
{:%, [],
[Ecto.Query.Tagged, {:%{}, [], [value: 1, type: {1, :id}]}]}]]},
params: [], file: "iex", line: 0]}]}]]}
マクロ展開後のコード
ast = quote do ~~~~ end
Macro.expand(ast, __ENV__) |> Macro.to_string
%{
__struct__: Ecto.Query,
assocs: [],
distinct: nil,
from: {Comment.__schema__(:source), Comment},
group_bys: [],
havings: [],
joins: [%JoinExpr{qual: :inner, source: nil, on: %Ecto.Query.QueryExpr{expr: true, params: [], line: 0, file: \"iex\"}, assoc: {0, :post}, file: \"iex\", line: 0, params: []}],
limit: nil,
lock: nil,
offset: nil,
order_bys: [],
prefix: Comment.__schema__(:prefix),
preloads: [],
select: %Ecto.Query.SelectExpr{expr: {:&, [], [0]}, params: [], file: \"iex\", line: 0, take: %{}},
sources: nil,
updates: [],
wheres: [%Ecto.Query.QueryExpr{expr: {:==, [], [{{:., [], [{:&, [], [1]}, :id]}, [], []}, %Ecto.Query.Tagged{value: 1, type: {1, :id}}]}, params: [], file: \"iex\", line: 0}]
}
defmacro is_nil(term) do
quote do
unquote(term) == nil
end
end
渡されたコードをASTに変換する
quote do
1 + 1
end
↓
{:+, [context: Elixir, import: Kernel], [1, 1]}
quote内にコードを埋め込む
defmacro my_if(clause, do: x, else: y) do
quote do
case clause do
nil -> unquote(y)
false -> unquote(y)
_ -> unquote(x)
end
end
end