Skip to content

Latest commit

 

History

History
135 lines (103 loc) · 3.34 KB

20.2.md

File metadata and controls

135 lines (103 loc) · 3.34 KB

20.2 マクロはコードを注入する

コンパイラは、コードを解析し、入れ子のタプルに変換する マクロでタプルを操作する

マクロは受け取った引数を評価しない

以下はそれ自身が内部表現になる

  • アトム
  • 数値
  • リスト(キーワードリスト含む)
  • バイナリ
  • 2要素のタプル

上記以外は「3要素のタプル」で表現される

goh@goh% iex "macros/dumper.exs"
Erlang/OTP 21 [erts-10.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

:atom
1
1.0
[1, 2, 3]
"binaries"
{1, 2}
[do: 1]
{:{}, [line: 20], [1, 2, 3, 4, 5]}
[
  do: {:__block__, [line: 23],
   [
     {:=, [line: 23], [{:a, [line: 23], nil}, 1]},
     {:+, [line: 23], [{:a, [line: 23], nil}, {:a, [line: 23], nil}]}
   ]}
]
[do: {:+, [line: 35], [1, 2]}, else: {:+, [line: 37], [3, 4]}]

内部表現 = 抽象構文木

[関数名のアトム, 行数を表すキーワードリスト, 引数]

ロードの順番

requireを使う理由

Kernel.SpecialForms.require(module, opts)

  • module: 先読みするモジュール
  • opts: オプション

マクロを実行するためにモジュールを要求する 現在のモジュールよりも先に require で指定されたモジュールをコンパイルする

マクロはプログラム実行前に展開される => require は、あるモジュールで定義されたマクロを他のモジュールで使用する際に使われる

モジュールを分ける理由

以下の条件を満たす場合、モジュールのどの部分からロードしてよいのかわからなくなり、エラーが発生する

  1. あるモジュールと、それを呼び出すコードが同じファイルにある
  2. モジュールが、それを呼び出すコードと同じスコープにある
goh@goh% iex "macros/dumper_load_error.exs"
Erlang/OTP 21 [erts-10.1.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe] [dtrace]

** (CompileError) macros/dumper_load_error.exs:11: module My is not loaded but was defined. This happens when you depend on a module in the same context in which it is defined. For example:

    defmodule MyApp do
      defmodule Mod do
      end

      use Mod
    end

Try defining the module outside the context that uses it:

    defmodule MyApp.Mod do
    end

    defmodule MyApp do
      use MyApp.Mod
    end

If the module is defined at the top-level and you are trying to use it at the top-level, this is not supported by Elixir

quoto関数

Kernel.SpecialForms.quote(opts, block)

  • opts: オプション
  • block: ブロック

ブロックの内部表現を返す

> quote do: :atom
:atom
> quote do: 1
1
> quote do: 1.0
1.0
> quote do: [1, 2, 3]
[1, 2, 3]
> quote do: "binaries"
"binaries"
> quote do: {1, 2}
{1, 2}
> quote do: [do: 1]
[do: 1]
> quote do: {1, 2, 3, 4, 5}
{:{}, [], [1, 2, 3, 4, 5]}
> quote do: (a = 1; a + a)
{:__block__, [],
 [
   {:=, [], [{:a, [], Elixir}, 1]},
   {:+, [context: Elixir, import: Kernel], [{:a, [], Elixir}, {:a, [], Elixir}]}
 ]}
> quote do: [do: 1 + 2, else: 3 + 4]
[
  do: {:+, [context: Elixir, import: Kernel], [1, 2]},
  else: {:+, [context: Elixir, import: Kernel], [3, 4]}
]

quote と「"」は似ている あとに続くものを [文字列|コード] と解釈し、 [適切な表現|内部表現] を返す