LoginSignup
22
2

More than 1 year has passed since last update.

Elixirでモジュールをインポートする際、importよりもaliasが望ましい理由について

Last updated at Posted at 2022-12-02

調べるきっかけになった事の発端は、同僚がimportについて公式のelixir-lang.orgを調べているときに、以下の記載を見つけ質問を受けたからです。

質問

import文の説明の末尾にはこんな記載がありますが、importって使わない方がいいんですか?

原文:Note that imports 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    

上記のような理由から、imposrtsしてはダメなのかと。sが付いてるのは、無闇にたくさんimportしてはダメってことなのかと腹落ちしました。

回答

上記理由から、importは最小限で、なるべくaliasを使った方がいいと思います。
ただし、importもaliasも多用すると、プログラムの可読性が落ちるので、適度に利用する方がいいと思います。

※訳は私が付けたものなので間違えなどありましたらご指摘お願いいたします。

22
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
2