どうも、靖宗です。
今回は若干気になってたモジュール(Modules)と関数(functions)。
今までなんとなく扱ってきましたが、きっちり読み解いて行きたいところ。
今までにもString.length
とかモジュールを使用してきましたが、モジュールは「複数の関数の集合」ということになるそうです。
まぁそうですよねという感じではありますが、おそらくオブジェクト指向との違いが出てくるのはこの辺では。
defmodule
というマクロでモジュールを宣言し、def
というマクロでモジュール内の関数を宣言するそうです。
iex> defmodule Math do ...> def sum(a, b) do ...> a + b ...> end ...> end iex> Math.sum(1, 2) 3
コンパイル(Compilation)
「コードが長くなるからスクリプトファイルに書いてコンパイルしなさい」とのことで、その流れでしょう。
まずはmath.ex
を作成し、中を書いていく
defmodule Math do def sum(a, b) do a + b end end
elixirc math.ex
Elixir.Math.beam
というファイルが同ディレクトリで生成されたら、そのディレクトリでiex
を実行。
PS > iex.bat Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help) iex(1)> Math.sum(1, 2) 3
おお~、使えた✌('ω')
基本的にElixirのプロジェクトでは以下のようなディレクトリ構成になるそうです。(おそらくベストプラクティス)
- ebin(コンパイル済みバイナリ)
- lib(Elixirのコード、*.ex)
- test(テストコード、*.exs)
実際にプロジェクトとかを作成していく際にはmix
と呼ばれるビルドツールを使うっぽいですが、まずは学習ということで泥臭くやっていきましょう的な事も書かれています。
たしかにQiitaの記事とかでmix
とか見た気がする・・・
Scripted mode
*.ex
と*.exs
の挙動の違い。
基本的にどちらもElixirのスクリプトファイルとして認識され、文法なども同じものらしいですが、*.exs
はコンパイル済みバイナリが生成されず逐次コンパイルしていくファイルとなるそうです。
あくまで、逐次コンパイルなのでコンパイルはされてるんですが、メモリ上にしか残らないみたいです。
どうりでテストコードに用いられる訳ですね。
一応例。math.exs
というファイル名で以下を作成。
defmodule Math do def sum(a, b) do a + b end end IO.puts Math.sum(1, 2)
実行。
PS > elixir math.exs
3
実行できました。*.beamファイルは生成されていません。
関数(Named functions)
名あり関数とか有名関数とかで訳すのか?とも思いましたがまぁ関数でええでしょう。
モジュール内で関数を宣言するときはdef/2
を使用し、外部から実行できないプライベート関数を宣言するときはdefp/2
を使用します。
(defのことをmacroと言ってみたり、functionと言ってみたり、どっちやねん)
defmodule Math do def sum(a, b) do do_sum(a, b) end defp do_sum(a, b) do a + b end end IO.puts Math.sum(1, 2) #=> 3と表示される IO.puts Math.do_sum(1, 2) #=> ** (UndefinedFunctionError)、エラーがでる
何の言及もありませんでしたが、elixirでは#
がコメントになるっぽいです。
実行してみる。
PS > elixir function.exs 3 ** (UndefinedFunctionError) function Math.do_sum/2 is undefined or private Math.do_sum(1, 2) function.exs:12: (file) (elixir) lib/code.ex:767: Code.require_file/2
関数はガードを使用した場合分け?や複数の句に対応してます。引数の個数や状態によって結果を変更するときに使用しそうです。
defmodule Math do def zero?(0) do true end def zero?(x) when is_integer(x) do false end end IO.puts Math.zero?(0) #=> true IO.puts Math.zero?(1) #=> false IO.puts Math.zero?([1, 2, 3]) #=> ** (FunctionClauseError) IO.puts Math.zero?(0.0) #=> ** (FunctionClauseError)
クエスチョンマークが関数名に入ってるのが気になりますが、真偽値を返す関数の命名規則っぽいです。
hexdocs.pm
どの関数にも当てはまらなければエラー発生。
一応def
もif
と同様ワンラインで書けるそうです。
defmodule Math do def zero?(0), do: true def zero?(x) when is_integer(x), do: false end
簡単なモジュールなら良いかもしれませんが、ちょっと複雑な処理などは極力複数ラインの書き方で対応しましょう。
ちょっと長いのでこの辺で一旦区切ります。