[翻訳] use, import, require --- Elixirではどういう意味なのか

Elixir言語の解説サイトLEARNING ELIXIRから、Joseph Kainさんの2016年1月20日付けの記事[Use, import, require, what do they mean in Elixir?](http://learningelixir.joekain.com/use-import-require-in-elixir/)の翻訳です。

モジュールを参照する宣言文は他の言語にもいろいろあります。どの言語でも「どう使い分けていいのか」結構迷いますね。ではElixirでは？
この記事はその疑問に答えるものです。タイトルに入ってないけど`alias`もありますよ。

-----

Elixirにはいくつか他のモジュールを参照するためのスペシャルフォームがあります。それには以下のものが含まれます:

* `use`
* `import`
* `require`
* `alias`

それぞれ独自の意味を持っているのですが慣れるまではどれを使えばいいのかなかなかわかりませんよね。

各スペシャルフォームの実演をするために新しいテスト用のプロジェクトを作りましょう。

```bash
$ mix new use_import_require
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/use_import_require.ex
* creating test
* creating test/test_helper.exs
* creating test/use_import_require_test.exs

Your Mix project was created successfully.
You can use "mix" to compile it, test it, and more:

    cd use_import_require
    mix test

Run "mix help" for more commands.
```

新しい空のモジュールができました。

```ex
# lib/use_import_require.exです
defmodule UseImportRequire do
end
```

この記事の後ろに進むに連れて`use`, `import`, `require` そして `alias`を入れて使っていきます。

## Elixirのalias

まずコードを打ちやすく(ということは多分読みやすく)することだけが目的の`alias`から。aliasを使うためには別のモジュールを用意しないといけません。では追加しましょう:

```ex
# lib/use_import_require/alias_me.exを作成してこう書きます
defmodule UseImportRequire.AliasMe do
  def function do
    IO.puts "#{__MODULE__}.function"
  end
end
```

さて、もしこの`UseImportRequire`の中の`function`を呼び出したい場合、完全修飾名で参照する必要があります:
`UseImportRequire.AliasMe.function`とね。しかし書くのが大変なのでこんな風に`alias`を使い、より短い名前で使うことができます:

```ex
defmodule UseImportRequire do
  alias UseImportRequire.AliasMe

  def alias_test do
    AliasMe.function
  end
end
```

さてこれです。これが`alias`の機能で、より短い名前を使うことができるというわけです。もちろんaliasで別名を使うように一つ以上のモジュールを同時に扱う更なる記法もあります。多重にネストしたモジュールをaliasで柔軟に扱うこともできます。しかし目的といえば、そう、`alias`は名前の短縮に使う、ということです。

`alias`を使うにあたっては当然トレードオフがあります。上記の簡単な例ではあまり考えることはありません。このアプリはほとんど全てが`UseImportRequire`から始まるのでそれを除外することは低いコストでノイズを除去してコードを読みやすくできますから。
しかしプロジェクト内のモジュールの数が増えていくにつれ、結局リーフ名が同じ[^1]モジュール名[^2]を付けることになってしまうかもしれません。そうなると別名を使うのは読む人を混乱させてしまうことになるかもしれません。
別名を使うと曖昧さを生み出してしまうのか？と自問することになるでしょう。
[^1]: Hogehoge.Taratara と Ponpoko.TarataraでのTaratara。Elixirの公式ドキュメントではLeaf nameという記載はなさそうだけど一般的なんでしょうか。
[^2]: 例えばUseImportRequire.AliasMeをlib/use_import_require/alias_me.exに、UseImportRequire2.AliasMeをlib/use_import_require2/alias_me.exに置く場合(alias_me.exだとピンとこないけど「read_data.ex」のようなモジュールがfrom_web/,from_sensor/,from_console/...の下に並ぶケースは多そう)に別名がどっちもAlias.Meになるしかない…と最初書いた時点では考えていたようですね。

*1月23日追記* : コメント欄でPhilip Clarenが指摘したように、alias-asフォームを使うと別名を使うときに名前を選べるようになります。以下の例を追加しました:

```ex
defmodule UseImportRequire do
  alias UseImportRequire.AliasMe
  alias UseImportRequire.AliasMe, as: AnotherName

  def alias_test do
    AliasMe.function
  end

  def alias_as_test do
     AnotherName.function
  end
end
```

見ればわかるように`as: name`パラメータを`alias`に追加できるので使いたい名前を別名に与えることができます。この例では`AnotherName`という名前にしました。

Alias-asは同じリーフ名を持つ２つ以上のモジュールがある場合にとても役立ちます。例えば`UseImportRequire.FirstKind.AliasMe`と`UseImportRequire.SecondKind.AliasMe`があるような場合、単純に両方を別名で扱うことはできません。そうすると曖昧さが残ってしまいます。alias-asを使うとそれぞれに違う名前を別名として選ぶことができます。

## Elixirのimport

さて、まだ`AliasMe.function`は書くには大変すぎると思ったとします。その場合は`import`を使いましょう。importの例を挙げるためにimportされるモジュールを新規に作ります:

```ex
# lib/use_import_require/import_me.ex
defmodule UseImportRequire.ImportMe do
  def function do
    IO.puts "#{__MODULE__}.function"
  end
end
```

そしてこれを`UseImportRequire`から参照してみます:

```ex
defmodule UseImportRequire do
  alias UseImportRequire.AliasMe
  import UseImportRequire.ImportMe

  def alias_test do
    AliasMe.function
  end

  def import_test do
    function
  end
end
```

これで単純に`function`で目的の関数を参照できました。短くていいですね。でも代わりに何か犠牲になってるんじゃ…？

このケースではずっと曖昧さを生み出しやすくなってしまっています。例えばもし`AliasMe`と`ImportMe`の両方のモジュールをインポートしたら結局どちらも`function`という関数を持つので、実のところコンパイルされないでしょう。なお、このコンパイルエラーは実際に曖昧な名前付けをされた関数が呼び出されるまで発生しない遅延型です。

私は`import`を使うのは控えることをおすすめします。あまりにも情報量がカットされてしまうのであなたのコードを読む人に負担になりかねないので。
でもインポートが役立ついくつかの場合もあります。あるモジュールが特定のモジュールを数多く呼び出すことに焦点を合わせているような場合なら`import`は有意義です。ひとつ一般的な例を挙げるならEctoのクエリを拡張して使うモジュールはよく`Ecto.query`をインポートしています。

`import`マクロはまた特定の関数やマクロをインポートすることができます。これによって「名前空間の汚染」を減らして曖昧さや混同の危険を減らすことができます。再び、これは`Ecto.Query`でよく使われる手法で、ドキュメントでの推奨は:

```ex
import Ecto.Query, only: [from: 2]
```

`Ecto.Query.from/2`マクロだけをインポートするためです。

## Elixirのrequire

`require`マクロはコンパイラにrequireマクロを含んでいるモジュールをコンパイルする前に指定されたモジュールをロードするように指示します。これは特定のモジュールからマクロを参照したい時のみに必要です。例えば以下の様な場合、:

```ex
defmodule UseImportRequire do
  require UseImportRequire.RequireMe
end
```
`RequireMe`モジュールが使いたいマクロを含んでいる場合に使います。これはマクロの名前については何も特別なことは行いません。やはり完全修飾名で参照しなければなりません。

この使い方では`require`と`import`の目的は少しかぶっています。どちらの他のモジュールのマクロを使うことができます。しかし名前空間に与える影響は異なっています。

## Elixirのuse

`use`マクロはスペシャルマクロ、`__using__/1`を特定のモジュールから呼び出します。以下に例を挙げます:

```ex
# lib/use_import_require/use_me.ex
defmodule UseImportRequire.UseMe do
  defmacro __using__(_) do
    quote do
      def use_test do
        IO.puts "use_test"
      end
    end
  end
end
```

そして以下の行を`UseImportRequire`に追加します:

```ex
use UseImportRequire.UseMe
```

`UseImportRequire.UseMe`は`__using__/1`マクロを呼び出すことで`use_test/0`関数を定義します。

これが`use`のやっていることの全てです[^3]。ところが`__using__/1`マクロが今度は`alias`や`require`や`import`を呼び出すこともよくあります。一方で`__using__/1`マクロが使われているモジュールが別名で参照されたりやインポートされることもあります。これによって使われるモジュールでその関数やマクロがどのように参照されるかのポリシーを決めることができます。`__using__/1`マクロが他のモジュールへの参照を設定できることは特にサブモジュールについて非常に柔軟性が高くなります。
[^3]: 端的には参照される側のモジュール内の`__using__/1`マクロで公開したい関数やマクロを設定できるということ

Phoenix Frameworkでは`use`と`__using__/1`マクロを`alias`や`import`をユーザー定義モジュールで繰り返し呼び出す手間を削減するために利用しています。

`Ecto.Migration`に短くていい例があります:

```ex
defmacro __using__(_) do
  quote location: :keep do
    import Ecto.Migration
    @disable_ddl_transaction false
    @before_compile Ecto.Migration
  end
end
```

`Ecto.Migration.__using__/1`マクロは`import`を含んでいるので`use Ecto.Migration`を使うことは`import Ecto.migration`もまた使うことになります。このマクロはまたEctoのビヘイビアを決めている(であろう)プロパティの設定も行っています。

要約すると、`use`マクロは指定されたモジュール内の`__using__/1`マクロを呼び出すだけである。何が行われているか理解するには`__using__/1`マクロの定義を読む必要がある。ということです。

## モジュールの参照

では、単に他のモジュールから関数を呼び出したくなったら上記のマクロのうちどれを使うのが正解でしょうか？答は…どれでもありません。その代わり直接参照ことができます。参照できるモジュールの例です:

```ex
# lib/use_import_require/reference_me.ex
defmodule UseImportRequire.ReferenceMe do
  def function do
    IO.puts "#{__MODULE__}.function"
  end
end
```
そしてこんな風にして関数にアクセスできます:

```ex
def reference_test do
  UseImportRequire.ReferenceMe.function
end
```
この通り。モジュールを`use`や`import`や`require`を使って参照する必要はありません。単に完全修飾名で呼ぶだけです。

## スコープ

`alias`や`import`を使う場合には便利さと明確さの間でトレードオフがあると述べました。このトレードオフを緩和するもうひとつのやり方があります。`alias`と`import`は今まで使ってきたようにモジュールの最も外側のスコープから呼び出す必要はないのです。例えば他の関数の内側から呼び出すことができます。`import`をそう使う例を挙げます:

```ex
defmodule UseImportRequire.WithScope do
  def scope_test do
    import UseImportRequire.ReferenceMe
    function
  end
end
```

曖昧さの問題から`UseImportRequire`モジュールではなくて新しいモジュールを作ってこれを追加しなければなりませんでした。

スコープありのバージョンだと他の関数からは完全修飾名を使わないと`function`にアクセスできません。例えば以下はコンパイルされないでしょう:

```ex
defmodule UseImportRequire.WithScope do
  def scope_test do
    import UseImportRequire.ReferenceMe
    function
  end

  def failing_scope_test do
    function
  end
end
```
`alias`マクロも関数内で同じように使えます。

`import`や`alias`を狭いスコープでこのように呼び出すことはあなたのコード内で発生する「名前空間の汚染」の総量を抑えることができます。

さらに、狭いスコープを使うことで`alias`や`import`を参照元の近くに置くことができ多少なりともコードを明白にでき、読む人が何が起きているか追いかけるのを簡単にします。特にあなたのコードが長くなり始めた時に有効です。

## 例

`import`を使ったいい例を挙げましょう:

```ex
defmodule Orthrus.Repo.Migrations.CreateUser do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :name, :string
      add :username, :string
      add :password_hash, :string
      add :email, :string

      timestamps
    end

  end
end
```

`use Ecto.Migration`の呼び出しは`Ecto.Migration.__using__/1`を呼び出します。先に見たとおり、このマクロは次は`import Ecto.Migration`を呼び出します。このインポートはマイグレーションについて非常に明白なコードを書けるようにしてくれます。`create`,`add`,`timestamps`を`Ecto.Migration`を頭に付けてコードを散らかさないでもよくしてくれるのです。

マイグレーションについてはマイグレーション自身が狭い範囲に集中されたタスクであることからよいトレードオフになりました。`create table`や`add`への参照を読むとき、データベースのマイグレーションを考えやすくなるのでこのコードは意味が取りやすいのです。

でももし他のそれほど集中されていないタスクがあるなら、`import`が最適な選択かどうか自問する方がいいと思います。

-----