URL
試した環境
- Ubuntu Server 14.04 LTS
- Erlang/OTP 18
- Elixir 1.0.4
alias, require and import
ソフトウェアと再利用するために、Elixirは3つディレクティブを提供している。
これらはレキシカルスコープを持っているためディレクティブと呼ばれる。
alias
alias
は任意のモジュール名のためエイリアスを設定することができるようにする。特別な数学的な操作をするために、特定のリストの実装にMath
モジュールを使うことを想像すると
def module Math do
alias Math.List, as: List
end
ここで、List
へのどんな参照も自動でMath.List
へ展開される。このケースでは、オリジナルのListにアクセスしたいが、それはモジュール名にElixir.
のプレフィックスをつけることによって可能になる。
List.flatten #=> uses Math.List.flatten
Elixir.List.flatten #=> usee List.flatten
Elixir.Math.List.flatten #=> uses Math.List.flatten
注意:Elixrで定義されているすべてもモジュールは、メインのElixirの名前空間内で定義される。しかし、利便性のために、それらを参照したい場合"Elixr"は省略可能である。
エイリアスはショートカットを定義する場合に頻繁に使われる。実際には、:as
オプションないでalias
を呼び出すと、モジュール名の最後の部分が自動でaliasに設定される。
alias Math.List
下記と同様
alias Math.List, as: List
alias
は、特定の関数内でエリアスを設定することが許されるレキシカルスコープであることに注意する。
defmodule Math do
def plus(a, b) do
alias Math.List
# ...
end
def minus(a, b) do
# ...
end
end
上記の例では、関数plus/2
の内部でalias
を呼び出している。
このalias
は関数plus/2
の内部でのみ有効。 minus/2
には何の影響もない。
require
コードを生成するためのコードを書くメタプログラミングのためのメカニズムとしてマクロを提供している。
マクロはコンパイル時に展開され、実行されるコードのかたまり。
これは、マクロを使うためそのモジュールと実装がコンパイル時に利用可能であること保証する必要があることを意味する。これは、require
ディレクティブで利用できる。
iex> Ingeger.is_odd(3)
** (UndefinedFunctionError) undefined function: Ingeger.is_odd/1 (module Ingeger is not available)
Ingeger.is_odd(3)
iex> require Integer
nil
iex> Integer.is_odd(3)
true
Elixirでは、Integer.is_odd/1
は、ガードとして使えるようにするためにマクロとして定義されている。これは、Integer.is_odd/1
を呼び出すために、初めにInteger
モジュールを呼び出す必要があることを意味している。
一般的に、モジュール内でマクロを使いたい場合、そのモジュールを使う前にrequreする必要はない。ロードされていないマクロを呼びだそうとした場合エラーが起きる。 allias
ディレクティブのように require
もレキシカルスコープであることに注意する。
import
他のモジュールで、完全修飾名を使うことなく関数やマクロに簡単にアクセスするために、 import
を使う。
例えば、 何度もList
モジュールの duplicate/2
関数を使いたい場合、importすることができる。
iex> import List, only: [duplicate: 2]
nil
iex> duplicate :ok, 3
[:ok, :ok, :ok]
このケースでは、List
モジュールから duplicate/2
関数だけをimportしている。 :only
はオプションだが、名前空間の内部にモジュールのすべての関数がimportされること防ぐために、このオプションを使う。
:expect
は、そのリストの関数を除いたモジュールのすべての関数をimport するためにのオプションを提供する。
import
は :only
オプションを与えることで :macros
と :functions
をサポートする。例えば、すべてのマクロをimport するためには、次の記述する
import Integer, only: :macros
もしくは、すべての関数をimportするには、次のように書く
import Integer, only: :functions
import
もレキシカルスコープ。これは関数の定義の内部にマクロや関数をimportできることを意味している。
defmodule Math do
def some_funtion do
import List, only: [duplicate: 2]
duplicate(:ok, 10)
end
end
上記の例では、インポートしたList.duplicate/2
は特定の関数内でしか見ることができない。 duplicate/2
はこのモジュール内の他のどんな関数からも利用できない。(もしくは、他のどんなモジュールからも)
モジュールをimport
すると、自動でrequire
することに注意する。
Aliases
Elixirエイリアスは何で、どうやって表現しているのかという点を不思議におもうかもしれない。
Elixirでのエイリアスは、コンパイル時にatomに変換される大文字始まりの識別子( String
, Keyword
など)である。例えば、String
エイリアスはデフォルトでは アトムの```:"Elixir.String"を変換している。
iex> is_atom(String)
true
iex> to_string(String)
"Elixir.String"
iex> :"Elixir.String" == String
true
alias/2
ディレクティブを使うことによって、簡単にエイリアスに変換することできる。
エイリアスは、Erlang VM (つまりElixir)でモジュールはatomとして表現されるためうまく動く。例えば、Erlangモジュールを呼び出すためのメカニズムは、
iex> :lists.flatten([1, [2], 3])
[1, 2, 3]
これは、モジュールの内部で与えられた関数を直接呼び出すことを許すメカニズムでもある。
iex> mod = :lists
:lists
iex> mod.flatten([1, [2], 3])
[1, 2, 3]
単に:list
アトム上のflatten
関数を呼び出しているだけ。
Nesting
nestingとElixir上でそれがどのように動作するかを話す。
次のような例を考える。
defmodule Foo do
defmodule Bar do
end
end
上記の例は、2つのモジュール Foo
と Foo.Bar
を定義している。2つ目の関数は、同じレキシカルスコープにある限り Foo
内の Bar
としてアクセスされる。
Bar
モジュールを Foo
モジュールの定義の外に移動した後、フルネーム( Foo.Bar
)によって参照される必要がある。もしくは、上記で議論したように、alias
ディレクティブの仕様をセットする必要がある。 Bar
モジュールの定義は変更される。
このコードは上記の例と同じ。
defmodule Foo.Bar do
end
defmodule Foo do
alias Foo.Bar, as: Bar
end
上記のコードは厳密には、次と同じ。
defmodule Elixir.Foo do
def module Elixir.Foo.Bar do
end
alias Elixir.Foo.Bar, as: Bar
end
注意:Elixirでは、言語はすべてのモジュール名をatomに変換するため、Foo.Bar
モジュールを定義できるようになる前に、Foo
モジュールを定義する必要はない。
モジュールの連続を定義することなく、任意にネストしたモジュールを定義できる。( Foo
の定義や Foo.Bar
の定義が初めになくても、Foo.Bar.Baz
を定義できる。)