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