LoginSignup
50
15

More than 5 years have passed since last update.

Elixirのifはタダの関数

Last updated at Posted at 2018-10-07

fukuoka.ex代表のpiacereです
今回もご覧いただいて、ありがとうございます:bow:

他言語では、「if」は「言語の構文」として実装されるのに対し、Elixirは「関数」として実装されています

それを実感するコラムを書いてみました

Elixirのifがタダの関数であることを確認する

iexを起動して、普通のifの動きを確認してください

iex> if "abc" == "abc", do: "abc is abc"
"abc is abc"
iex> if "abc" == "xyz", do: "abc is xyz"
nil

do句を無くすと、以下のエラーメッセージが返ってきます

iex> if "abc" == "abc"
** (CompileError) iex: undefined function if/1

「undefined function if/1」、つまり、ifという関数は、1つの引数を取るバージョンが無い、ということを意味しています

つまり、こんな関数呼出をしたのと同じだった、ということです

iex> if( "abc" == "abc" )
** (CompileError) iex: undefined function if/1

では、このメッセージを信じて、2つの引数を渡してみると…

iex> if( "abc" == "abc", "hoge" )
** (ArgumentError) invalid or duplicate keys for if, only "do" and an optional "else" are permitted
    (elixir) lib/kernel.ex:2920: Kernel.build_if/2
    (elixir) expanding macro: Kernel.if/2
    iex:28: (file)

どうやら、キーとして、「do」もしくは「else」以外は、許可されていないことが分かります

であれば、doをキーとした処理を渡してみましょう

iex> if( "abc" == "abc", do: "abc is abc" )
"abc is abc"
iex> if( "abc" == "xyz", do: "abc is xyz" )
nil

冒頭のifと同じことができるようになりました

上記ifを関数プロトタイプとして書くと、こんな感じで、まんま関数なことが分かりました

if( condition, do: true_execution )

アリティを指定して確認してみる

前々回のコラムでやった「アリティ」を使って、引数の個数を指定して、直接、調べてみましょう

iex> &if/1
** (UndefinedFunctionError) function :erl_eval.if/1 is undefined or private
iex> &if/2
** (ArgumentError) invalid or duplicate keys for if, only "do" and an optional "else" are permitted
    (elixir) lib/kernel.ex:2920: Kernel.build_if/2
    (elixir) expanding macro: Kernel.if/2
    iex:17: (file)

上述したものと同じエラーが返ってくることが確認できました

関数として、if/1は存在せず、if/2は存在しています

elseはどう実装されているか?

次に、elseについても調べてみましょう

iex> if( "abc" == "abc", do: "abc is abc", else: "abc is not abc" )
"abc is abc"
iex> if( "abc" == "xyz", do: "abc is abc", else: "abc is not abc" )
"abc is not abc"

さて、この「do: ~」と「else: ~」を見ると、3つの引数を取るifが、存在しているかのように見えます

関数プロトタイプとして書くと、こんな感じでしょうか

if( condition, do: true_execution, else: false_execution )

では、アリティで確認してみましょう

iex> &if/3
** (UndefinedFunctionError) function :erl_eval.if/3 is undefined or private

おや?引数を3つ取るifは、存在していないようです…

では、どうやって、elseを実装しているのでしょう?

ここで、関数プロトタイプを見てみると、Elixirで許可されていない構文が使われていることに気付きます

以下をiexで実行してみましょう

iex> do: "hoge"
** (SyntaxError) iex: syntax error before: do
iex> hoge: "hoge"
** (SyntaxError) iex: syntax error before: hoge

このタイプの記述が許されるのは、キーワードリスト内か、マップ内に限定されます

iex> [ do: "hoge" ]
[do: "hoge"]
iex> %{ do: "hoge" }
%{do: "hoge"}

ifの第2引数に、キーワードリストと、マップを渡してみて、確認しましょう

iex> if( "abc" == "abc", [ do: "abc is abc" ] )
"abc is abc"
iex> if( "abc" == "abc", %{ do: "abc is abc" } )
** (ArgumentError) invalid or duplicate keys for if, only "do" and an optional "else" are permitted
    (elixir) lib/kernel.ex:2920: Kernel.build_if/2
    (elixir) expanding macro: Kernel.if/2
    iex:40: (file)

どうやら、キーワードリストが正解のようです

ifの「do:」と「else:」はキーワードリスト

「do:」がキーワードリストであることが確認できたので、「else:」もキーワードリストに並べてみましょう

iex> if( "abc" == "abc", [ do: "abc is abc", else: "abc is not abc" ] )
"abc is abc"
iex> if( "abc" == "xyz", [ do: "abc is abc", else: "abc is not abc" ] )
"abc is not abc"

うまく動きましたー

これを踏まえて、関数プロトタイプを書くと、2つの引数を取る関数で、第2引数は「do:」と「else:」がキーとして許可されたキーワードリストとなります

if( condition, [ do: true_execution, else: false_execution ] )

ifをiex内ヘルプで確認してみる

最後にタネ明かしですが、iex内で、「h ~」とすると、ifの正体が確認できます

iex> h if
* defmacro if(condition, clauses)

Provides an `if/2` macro.

This macro expects the first argument to be a condition and the second
argument to be a keyword list.
 …

if/2は、defmacroで定義されており、第2引数をキーワードリストとして扱っていることが明かされています

ifもKernelモジュールの関数

if/2は、前々回のコラムで扱った「+」と同様、Kernelモジュールの関数(マクロ)として定義されていることが、以下リファレンスで確認できます
https://hexdocs.pm/elixir/Kernel.html#if/2

iex内ヘルプでも、Kernel無のifと同一であることが確認できます

iex> h Kernel.if
* defmacro if(condition, clauses)

Provides an `if/2` macro.

This macro expects the first argument to be a condition and the second
argument to be a keyword list.
 …

p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の image.pngimage.png のクリックをお願いしますー:bow:

50
15
7

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
15