import/2
macro(以下 import/2
)の except
オプションについて理解が正しくなかったため正しく理解した内容をもとに整理する。
早い話がドキュメントを正しく見ておけばよかったという話ではあるが、ここでは自分の中で飲み込んだ内容を記載する。import/2
の公式ドキュメントはこちら Elixir version 1.2。
まず import/2
macro について
Elixir の import/2
を使うと import した module の関数や macro を、Module.
の prefix なしで呼び出すことができるようになる。
例えば List.first/1
を例にとると以下のようになる。
iex> List.first([1])
1
iex> first([1])
** (CompileError) iex:4: undefined function first/1
iex> import List
nil
iex> first([1])
1
import/2
を使うことでショートカットが提供されるイメージだ。
さらに import/2
に渡すオプションを変更することで、特定の関数または macro のみを import したり、除外したりすることが可能になる。
下記は only
オプションの例で、List.first/1
を指定しているため、それ以外の関数/macro は import されず、List.last/1
は List.
なしでは呼び出すことができない。
iex> import List, only: [first: 1]
nil
iex> first([1])
1
iex> last([1])
** (CompileError) iex:2: undefined function last/1
同様に except
オプションの例が以下であり、List.first/1
以外を import しているため List.
なしで List.first/1
を呼び出せない。
iex> import List, except: [first: 1]
nil
iex> last([1])
1
iex> first([1])
** (CompileError) iex:3: undefined function first/1
import/2
は後から呼び出し可能
import/2
は後から呼び出して、動作を変えられる。これは公式のドキュメントにも例として載っている。公式からの引用だが、例えば以下のようにすることができ、flatten/1
を後から除外することができる。
import List, only: [flatten: 1, keyfind: 4]
import List, except: [flatten: 1]
試しに Macro.Env
の状態がどう変わるのかを見ることで import/2
の挙動を追ってみる。Macro.Env
にはコンパイル時の環境が格納されており、現在の状態は __ENV__
にてアクセスできる。__ENV__
には例えば現在の module、file 行数、require している module などの情報が含まれている。import を行うことで Macro.Env.functions
、Macro.Env.macros
の情報が変更されるので、この状態推移を見ることで import/2
の挙動を観測できる。
最初の例と同様に List.first/1
で only
した後に、except
で除外してみると以下のようになった。Macro.Env
全体を出力すると見づらいため、関連する List
module 部分のみを表示するようにしているが、後で呼び出した except
オプション付きの import/2
呼び出しによって List.first/1
が除外されている様子が分かる。
iex> __ENV__.functions |> Enum.filter(fn({x, y}) -> x == List end)
[]
iex> import List, only: [first: 1]
nil
iex> __ENV__.functions |> Enum.filter(fn({x, y}) -> x == List end)
[{List, [first: 1]}]
iex> import List, except: [first: 1]
nil
iex> __ENV__.functions |> Enum.filter(fn({x, y}) -> x == List end)
[]
私が一番勘違いしていたところはこの点で、これを理解していなかったばかりに PowerAssertEx の実装で macro ガチャガチャのよくない実装をしてしまっていた。
ちなみに良くない実装の部分については v0.0.8 で解消している。
何故勘違いしていたかというと import/2
を何となく理解しており、てっきり先に import されてしまったものは元に戻らないと考えていたため。関係ないけど最近子供達がカルタに興味を持ち出して私にクイズを出してくるのだが、急いては事を
と言われると何かツラいものを感じる。
まとめ
import/2
は何度も呼び出し可能で、後で呼び出したものの指定が優先される。
参考
- 公式ドキュメント