調べるきっかけになった事の発端は、同僚がimportについて公式のelixir-lang.orgを調べているときに、以下の記載を見つけ質問を受けたからです。
質問
import文の説明の末尾にはこんな記載がありますが、importって使わない方がいいんですか?
原文:Note that import
s are generally discouraged in the language. When working on your own code, prefer alias
to import
.
訳:この言語では、複数のインポートは一般的に使わない方が無難なので注意する。あなた自身のコードを書く場合は、importよりaliasを選ぼう。
参考:元のelixir-lang.org ドキュメント
考察
そんな事はないけど・・・公式サイトに書いているしなあ・・・と悩みつつ、
Google先生に翻訳頼むと、discouragedを非推奨と訳してくれるので、公式でOUTなのかとさらに悩みつつ、いや、訳がおかしくないか、非推奨といえどそこまで厳しく捉える必要はなく、使わない方が無難みたいな言い回しかなと思い返し、hexのimportドキュメントも参照。上記のような記載は一切なかったのでさらに詳しく調べました。
そこで、stackoverflowで同じことで質問しているインドの方を見つけました。
ポーランドの人の回答によると3つくらい例を挙げてくれています。
(原文)
①import creates compile time dependency between these modules, which mean that importing module compilation need to wait until imported module is compiled. alias do not create such dependency.
②import will make all calls to imported functions behave like local calls, while alias will make them still behave like remote calls. That is pretty important difference, but way beyond scope of this question. In short - local calls "do not work" with hot upgrades.
③imports often bring too much into scope of the module and can cause compilation conflicts when the imported module will add more functions (you cannot define function with the same name as the imported function).
(訳)
①import
はコンパイル時に待ち時間あるけど、alias
にはそれがない。
②import
は、インポートされた関数のすべての呼び出しをローカル呼び出しのように動作させるが、alias
はそれらをリモート呼び出しのように動作させます。つまり、import
はローカル呼び出しなので、呼び出し先のホットアップグレードは出来ません。
③import
は時々モジュールのスコープに入りすぎて、インポートされたモジュールがさらに関数を追加するときにコンパイルの競合を引き起こす可能性があります (インポートされた関数と同じ名前の関数を定義することはできません)。
参考:元の質問
それ以外にも調べるとこんな弊害もあるそうです。
④import
に限らずalias
などもですが、コードが読みやすくなる反面、情報がカットされ過ぎて困惑する
さて、①②④は文章読めば納得。
③はどんな状況なのかテストしてみました。
iexでテストします。
String.length
とカーネルのlength
は競合しそうなので、それを試します。
iex> import String
String
iex> length([1,2,3])
** (CompileError) iex:2: function length/1 imported from both String and Kernel, call is ambiguous
(elixir 1.14.0) src/elixir_dispatch.erl:149: :elixir_dispatch.expand_import/7
(elixir 1.14.0) src/elixir_dispatch.erl:119: :elixir_dispatch.dispatch_import/6
(elixir 1.14.0) src/elixir.erl:364: :elixir.quoted_to_erl/3
(elixir 1.14.0) src/elixir.erl:274: :elixir.eval_forms/3
(elixir 1.14.0) lib/module/parallel_checker.ex:100: Module.ParallelChecker.verify/1
(iex 1.14.0) lib/iex/evaluator.ex:329: IEx.Evaluator.eval_and_inspect/3
(iex 1.14.0) lib/iex/evaluator.ex:303: IEx.Evaluator.eval_and_inspect_parsed/3
ああ、エラー出ますね。
(原文)function length/1 imported from both String and Kernel, call is ambiguous
(訳)関数length/1は文字列とカーネルの両方からインポートされ、呼び出しはあいまいです。
たしかに、カーネルのlength
なのかString
関数のlength
なのか分からないよね。
import
にある:except
オプションを使えば回避できます。
iex> import String, except: [length: 1]
String
iex> length([1,2,3])
3
上記のような理由から、imposrt
sしてはダメなのかと。sが付いてるのは、無闇にたくさんimport
してはダメってことなのかと腹落ちしました。
回答
上記理由から、importは最小限で、なるべくaliasを使った方がいいと思います。
ただし、importもaliasも多用すると、プログラムの可読性が落ちるので、適度に利用する方がいいと思います。
※訳は私が付けたものなので間違えなどありましたらご指摘お願いいたします。