Elixirの関数には引数によって処理を分ける機能(Guard節)というのが存在する。
このガード節、結構いろいろな条件で処理を分岐できるのですが引数の型でも処理分けできるので今日はそれを試してみる
準備する
とりあえずライブラリ生成のお約束に則って
# Bundle gemみたいなコマンド
mix new hoge
# 生成されたディレクトリへ
cd hoge
# とりあえずテストを回しておこう
mix test
テストする
Elixirはテスト用のライブラリがしっかり入っているのでそれを持って先にテストを書いておく
とりあえず今回は引数を渡すと引数の型名を文字列で返してくれる関数を実装するということで
テストはこんな感じに
# test/hoge_test.exs
test 'Hoge.type関数は引数の型名を文字列で返す' do
assert Hoge.type(:a) == "Atom"
assert Hoge.type(nil) == "Nil"
assert Hoge.type(1) == "Int"
assert Hoge.type(1.1) == "Float"
assert Hoge.type(true) == "Boolean"
assert Hoge.type("s") == "String"
assert Hoge.type([1,2,3]) == "List"
assert Hoge.type(%{a: 1}) == "Map"
assert Hoge.type({:A, 1}) == "Tupple"
assert Hoge.type(fn -> IO.puts :a end) == "Function"
end
テストがかけたのでとりあえず Hoge
モジュールに何もしない関数を書いてからテストを流すか
# lib/hoge.ex
def type(x) do
end
これでテストを流す
テストコマンドは mix test
mix test
1) test 1 is number (HogeTest)
test/hoge_test.exs:13
Assertion with == failed
code: assert Hoge.type(1) == "Int"
left: nil
right: "Int"
stacktrace:
test/hoge_test.exs:14: (test)
...
ちゃんと落ちることが確認できた
実装する
テストが失敗することがわかったので実装する
def type(x) when is_integer(x), do: "Int"
def type(x) when is_float(x), do: "Float"
def type(x) when is_bitstring(x), do: "String"
def type(x) when is_boolean(x), do: "Boolean" # NOTE: Booleanは内部的にはAtomなのでAtomより先に評価しないとAtom判定される
def type(x) when is_nil(x), do: "Nil" # NOTE: Nilは内部的にはAtomなのでAtomより先に評価しないとAtom判定される
def type(x) when is_atom(x), do: "Atom"
def type(x) when is_list(x), do: "List"
def type(x) when is_tuple(x), do: "Tupple"
def type(x) when is_map(x), do: "Map"
def type(x) when is_function(x), do: "Function"
気をつけないといけないのは Boolean
と Nil
と Atom
の関係
これ試して初めて気づいたけどどうもBooleanとNilはAtomで表現されているらしい
なのでBooleanやNilよりもAtomのGuard節を上に書くと悲しいことに全部Atomとして結果が返ってくる
とりあえず実装はできたと思うのでテストを流す
mix test
Finished in 0.04 seconds
1 doctest, 1 tests, 0 failures
良さそう!
感想
引数ベースで処理を分けるのは他の言語だとバッドプラクティスという話を聞くけどElixirもといErlangがそういう言語仕様だからこうなるのは仕方ないね
せっかく言語にこういう書き方が実装されてるんだから積極的に使っていきたい所存
今回は条件分けに等値演算子などは使わなかったですが本当はそういうのを使ってもっといろいろな条件分岐していくこともできるそうな
いいなぁ...