本稿は「function_exported?/3 する前には Code.ensure_loaded/1 を呼び出そう」をもとに加筆・補正し、文章を整えました。
function_exported?/3でビヘイビアの関数の実装を確かめる
ビヘイビアが実装されたモジュールから、それらの関数を呼び出すときは、予めfunction_exported?/3によりその関数が備わっているかどうか確かめます。たとえば、つぎのようなFooビヘイビアを実装したFooImplモジュールがあるとします。
defmodule Foo do
@callback foo() :: :ok
end
defmodule FooImpl do
@behaviour Foo
@impl Foo
def foo() do
:ok
end
end
呼び出す関数からは、つぎのようにfunction_exported?/3で引数のモジュールの関数がエクスポートされているか、つまりFooビヘイビアが実装されているかを確認してから呼び出します。これでまったく問題のないコードにみえるかもしれません。
defmodule Bar do
def call_foo(mod) do
if not function_exported?(mod, :foo, 0) do
raise "Foo behaviour is not implemented"
end
mod.foo()
end
end
call_foo(FooImpl)
しかし、このコードは、mixから起動したときに動作しない場合があります。
なぜ動作しないのか
ドキュメントのfunction_exported?/3の項には、つぎのような注意書きがあります。引数のモジュールが予めロードされていない場合、function_exported?/3はモジュールの読み込みはしません。つまり、関数が実装されていない場合だけではなく、モジュールがまだロードされていないときもfalseが返されるのです。
Note that this function does not load the module in case it is not loaded.
モジュールを用いるには、そのモジュールが事前にErlang VMにロードされていなければなりません。それを理解するには、Erlangがモジュールをロードする戦略を知る必要があります。戦略はつぎのふたつです(Erlang VMのデフォルトは後者)。
- 組み込みモード アプリケーションを起動したときに、すべてのモジュールがロードされます。
- 対話モード 最初にそのモジュールの関数を呼び出したときに、そのモジュールがロードされます。
mixからアプリケーションを動かした場合には 対話モードになります。そのため、前掲のコードを動かしたとき、FooImplモジュールの関数をまだどれも呼んでいなければ、function_exported?/3はfalseを返してエラーになるのです。他方で、先に実行されたコードがFooImplモジュールの関数を呼び出していると、function_exported?/3の戻り値はtrueとなり、正しくmod.foo()が呼ばれます。
つまり、mixから起動したときは、どのコードをとおったかによって動作は変わってしまいます。これが「動作しない場合」があると述べた理由です。なお、Distilleryで生成すると、デフォルトで組み込みモードになります。
予めモジュールのロードを確かめる
ドキュメントのfunction_exported?/3の項は、Code.ensure_loaded/1の参照を促します。引数のモジュールがロードされているかどうか確かめて、結果がまだであればロードしてくれる関数です。つぎのようにCode.ensure_loaded/1の呼び出しを書き加えると、ビヘイビアのモジュールの読み込みが実行されます。
defmodule Bar do
def call_foo(mod) do
Code.ensure_loaded(mod)
if not function_exported?(mod, :foo, 0) do
raise "Foo behaviour is not implemented"
end
mod.foo()
end
end
call_foo(FooImpl)
もっとも、モジュールの読み込みは、何らかの理由で失敗するかもしれません。そのときの戻り値は{:error, reason}になりますので、エラーの分岐を書いた方がより厳密です(成功であれば{:module, module})。もっとも、その場合にはつぎのfunction_exported?/3がfalseを返して、結局ビヘイビアを実装していないというエラーになります。この例ような簡単なコードでしたら、これで済むこともあるでしょう。
Top comments (0)