cf. 8 モジュール – Modules – Elixir
モジュール
Elixir では関数をモジュールの中に入れてグループ化する。モジュールは defmodule
マクロで定義し、その中で def
マクロを使って関数を定義する。またマクロって単語が出てきたけど気にしない。
iex(1)> defmodule Math do
...(1)> def sum(a, b) do
...(1)> a + b
...(1)> end
...(1)> end
{:module, Math,
<<70, 79, 82, 49, 0, 0, 4, 200, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 157,
131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:sum, 2}}
iex(2)> Math.sum(1, 2)
3
モジュールの関数を呼び出すときは「モジュール名.関数名」だな。
コンパイル
モジュールをファイルに書いて保存しておき、コンパイルすると再利用しやすくなる。例えば次のようなファイルを作る。拡張子は ex。
defmodule Math do
def sum(a, b) do
a + b
end
end
このソースファイルは elixirc
コマンドで Erlang VM のバイトコードにコンパイルできる。
^o^ > elixirc math.ex
^o^ > ls
Elixir.Math.beam hello.exs math.ex
Elixir.Math.beam っていうファイルが、コンパイルされたバイトコード。ここで iex
を立ち上げると、Math
モジュールが使えるようになる。カレントディレクトリのバイトコードは自動的に読み込まれるってわけだ。
^o^ > iex
Eshell V8.0 (abort with ^G)
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Math.sum(1, 2)
3
スクリプトモード
コンパイルを必要としないのがスクリプトモード。こないだの記事で Hello, world プログラムを作ったのがこれ。こっちは拡張子を exs にする。ex ファイルと exs ファイルは中身的には区別はなく、違うのは目的。ex はコンパイルされるのを意図しているのに対して、exs はコンパイルせずにスクリプトモードで使用する。
defmodule Math do
def sum(a, b) do
a + b
end
end
IO.puts Math.sum(1, 2)
上のスクリプト math.exs を実行すると次のように出力される。おっと、さっき作った Elixir.Math.beam を消しておくのを忘れないように。そうでないと「Math モジュールを再定義しようとしてる」とかいう意味(たぶん)の warning が出る。
^o^ > rm Elixir.Math.beam
^o^ > elixir math.exs
3
Elixir スクリプトの実行は elixir
コマンドね。
名前付き関数
モジュールの中で、def/2 を使って関数を定義するのは上でもやった。この関数はモジュールの外からも呼べる関数になる。一方、defp/2 を使って定義するとモジュールの外からは呼べないプライベート関数になる。これらは名前付き関数と呼ばれる、たぶん、文脈からすると。
defmodule Math do
def sum(a, b) do
a + b
end
defp do_sum(a, b) do
a + b
end
end
IO.puts Math.sum(1, 2)
IO.puts Math.do_sum(1, 2)
上のスクリプトでは、関数 sum とプライベート関数 do_sum を定義して、呼び出している。これを実行すると:
^o^ > elixir math2.exs
warning: function do_sum/2 is unused
math2.exs:6
3
** (UndefinedFunctionError) function Math.do_sum/2 is undefined or private
Math.do_sum(1, 2)
math2.exs:13: (file)
(elixir) lib/code.ex:363: Code.require_file/2
こうなる。
Elixir の関数宣言(宣言て書いてあるな)は、ガードと複数句に対応している。例えばこんなふうに:
defmodule Math do
def zero?(0) do
true
end
def zero?(x) when is_number(x) do
false
end
end
IO.puts Math.zero?(0)
IO.puts Math.zero?(1)
^o^ > elixir math3.exs
true
false
関数のキャプチャ
関数を値として取り出すことをキャプチャと言い、&
を頭につける。math.exs を iex
で使ってみよう。
^o^ > iex math.exs
Eshell V8.0 (abort with ^G)
3
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> fun = &Math.sum/2
&Math.sum/2
iex(2)> fun.(1, 3)
4
キャプチャ構文は関数を作るときのショートカットとしても使える。この場合 &1
は1番目の引数を意味する。
iex(3)> fun = &(&1 + 1)
#Function<6.52032458/1 in :erl_eval.expr/5>
iex(4)> fun.(2)
3
デフォルト引数
名前付き関数はデフォルト値を持つ引数をとることができる。
defmodule Concat do
def join(a, b, sep \\ " ") do
a <> sep <> b
end
end
IO.puts Concat.join("hello", "world")
IO.puts Concat.join("hello", "world", "-")
^o^ > elixir concat.exs
hello world
hello-world