簡単Elixirシリーズ
~ マクロを理解しよう quoteとunquoteが必要な理由~
この記事は「Elixir Advent Calendar 2022」6日目の記事です
東京にいるけどfukuokaexのYOSUKEです。
簡単 Elixirシリーズでは小ネタをサクッと書いていこう。というコンセプトで作っていきます。
今回は、Elixir School のメタプログラミングの記事を見ていると、
quoteとunquoteを知ったらマクロを理解できる準びができた的な事が書かれていたので、
マクロって何? を理解する。という小ネタ(そう、この記事はサクッとがコンセプトW)
で、マクロについて色々調べて見ると、@yasuhiro-okada-aktskさんが、すでにElixir: Macro入門Qiitaの記事にしてくれているようでしたので詳しい内容はこちらを見ていただくことにして、ここではマクロを作る上でquoteとunquoteを使う理由が気になったのでその点について調べてみました。
まず、最初に公式ドキュメントでもマクロのページで最初に言われているのでとても大事な事なのでしょう。を紹介しておきます。
マクロは最後の手段としてのみ使用してください。明示的は暗黙的よりも優れていることに注意してください。明確なコードは簡潔なコードよりも優れています。
要するに多用するな! 作るなら相当な責任と覚悟をしなさい。と言うことでしょう。断言しましょう。僕レベルでマクロを扱おうなんて、なんて烏滸がましい!と言われた気持ちですw
マクロを理解しよう。
まず、マクロを理解する為に、Elixir Schoolに unless を利用した例が書かれていましたので、そちらを確認していきます。
unlessについてはこちらで記事にしてあるので詳しくない方はこちらもご覧ください。
一旦、Elixirスクールで紹介されている通りに書いてみます。
defmodule MyMacro do
defmacro unless(expr, do: block) do
quote do
if !unquote(expr), do: unquote(block)
end
end
end
iex()> MyMacro.unless true, do: "Hello"
nil
iex()> MyMacro.unless false, do: "Hello"
"Hello"
ちなみに、以下でも動きます。なので quote と unquote が必要な理由が知りたいです。
defmodule MyMacro do
defmacro unless(expr, do: block) do
if !(expr), do: block
end
end
iex()> MyMacro.unless true, do: "Hello"
nil
iex()> MyMacro.unless false, do: "Hello"
"Hello"
と言うことで、調べてみました。理由は簡単だったのですが、quote
とunquote
を利用しないバージョンで例えば以下のように flag
に true や false を入れて利用したいとします。 要するに変数を使った利用です。
iex()> flag = true
true
iex()> MyMacro.unless flag, do: "Hello"
nil
iex()> flag = false
false
iex()> MyMacro.unless flag, do: "Hello"
nil
このように、変数を入れても結果が変わりません。これはコンパイル時点で固定の値になってしまう為です。
と言うことで、コンパイル時にマクロはquoteを利用してASTで返すようにし、unquoteを利用してASTに、今回の場合であれば、引数の値が入るように作る必要があるということがわかりました。