マクロ、quate関数を使って、コードを内部表現に展開すると、コンパイル結果(タプル)に、自動的には追加されなくなる
内部表現を注入する方法は2つ
- マクロを使う
Code.eval_quoted
を使う
「20.6 コード片を走らせる他の方法」参照
- マクロに引数の内部表現が渡される
- マクロで処理する
マクロで評価された最後の式を返す - プログラムの内部表現にマクロの戻り値を差し込む
- 差し込まれたプログラムの実行結果をマクロの呼び出し元に返す
マクロの呼び出し元には、マクロの戻り値(内部表現)を実行した結果が返される
goh@goh% iex "macros/eg.ex"
Erlang/OTP 21 [erts-10.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
{{:., [line: 10], [{:__aliases__, [line: 10], [:IO]}, :puts]}, [line: 10],
["hello"]}
hello
「IO.puts("hello")」の内部表現を実行した結果、すなわち「hello」が呼び出し元に返される
goh@goh% iex "macros/eg1.ex"
Erlang/OTP 21 [erts-10.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
{{:., [line: 10], [{:__aliases__, [line: 10], [:IO]}, :puts]}, [line: 10],
["hello"]}
Different code
quote関数で「IO.puts "Different code"」の内部表現が返され、呼び出し元ではそれが実行される
既存のコードをquoteブロックに差し込む方法は2つ
- unquote関数を使う
- bindingを使う
Kernel.SpecialForms.unquote(expr)
- expr: 内部表現
goh@goh% iex "macros/eg2.ex"
Erlang/OTP 21 [erts-10.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
warning: variable "code" is unused
macros/eg2.ex:2
warning: variable "code" does not exist and is being expanded to "code()", please use parentheses to remove the ambiguity or change the variable name
macros/eg2.ex:11
** (CompileError) macros/eg2.ex:11: undefined function code/0
expanding macro: My.macro/1
macros/eg2.ex:11: Test (module)
quoteブロック内は、通常のコードとしてパースされる
quoteブロック内で、内部表現を処理してほしい場合は unquote
を使う必要がある
goh@goh% iex "macros/eg2.ex"
Erlang/OTP 21 [erts-10.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
hello
:ok
quote関数では、ブロック内のコードをパースして内部表現に変換している unquote関数では、関数内のコードをパースせず、そのままコピーする
unquote関数内のコードは、遅延評価されるとも言える quote関数のブロックがパースされるときではなく、内部表現が生成されるときに実行される
"sum=#{1 + 2}"
は、「1 + 2」を評価し、結果を文字列に埋め込む
quote do: def unquote(name) do end
は、nameの中身をquoteが返す内部表現に埋め込む
Kernel.SpecialForms.unquote_splicing(expr)
- expr: リスト
指定されたリストを展開する
> Code.eval_quoted(quote do: [1, 2, unquote([3, 4])])
{[1, 2, [3, 4]], []}
> Code.eval_quoted(quote do: [1, 2, unquote_splicing([3, 4])])
{[1, 2, 3, 4], []}
> Code.eval_quoted(quote do: [?a, ?=, unquote_splicing('1234')])
{'a=1234', []}
goh@goh% iex "macros/myif2.exs"
Erlang/OTP 21 [erts-10.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]
1 != 2
My.if
は条件とキーワードリストを受け取る- キーワードリストから
do:
、else:
を取り出す - quoteブロックを開く
unquote
を使って条件を注入する- false or nil =>
do:
- true =>
else:
- false or nil =>
unquote
を使ってdo:
またはelse:
に対応するコードを実行する