6章は「モジュールと名前付き関数」である。
ここでやっと普通の関数定義の説明がある。
で、Rubyっぽいこんな感じだ。でも、Rubyならdo
はいらない
defmodule Times do def double(n) do n * 2 end end
しかし、do...endはシンタックスシュガーで、do: (...)が基本的な構文らしい。
つまり、def はキーワード引数で:doにブロックを取るような関数だと。ほー・・・。
上の定義はこう書いてもいいらしい(が、「頼むからやめてくれ」と書いてあるw)
defmodule Times, do: (def double(n), do: n * 2)
なるほど。
関数の引数でパターンマッチしたい場合は、単に関数を複数書く。 例えば、階乗の計算はこんな感じ。上からマッチが試されるので、順番大事。
defmodule Factorial do def of(0), do: 1 def of(n), do: n * of(n-1) end
パターンマッチではなく、引数の型や値の評価で実行する関数を区別したい場合はガード節を付ける。こんな感じ。
defmodule Factorial do def of(0), do: 1 def of(n) when is_integer(n) and n > 0 do n * of(n-1) end def of(_), do: :error endend
OCamlと比べると、型によるパターンマッチが出来ないのは面倒くさく感じなくもない。 型それ自体が面倒くさいという意見もあると思うけど(笑)。
次に出てくるのは|>
で書く、パイプライン。メソッドチェーンの関数言語版だ。
昔、Rubyにも提案されていて、trunkにサクッと入ってたことはあるけど、どうも上手く行かなかったらしい。
それはともかく、例えば、「1から10までの数字を2倍して、5より大きいもの」を表現するのに、パイプラインがないと
iex> Enum.filter(Enum.map(1..10, &(&1 * 2)), &(&1 > 5)) [6, 8, 10, 12, 14, 16, 18, 20]
こうなっちゃうけど、パイプラインがあれば、
iex> (1..10) |> Enum.map(&(&1 * 2)) |> Enum.filter(&(&1 > 5)) [6, 8, 10, 12, 14, 16, 18, 20]
うん。明らかにこっちの方が見やすい。
ちなみにRubyでは
irb> (1..10).map{_1 * 2}.filter{_1 > 5} => [6, 8, 10, 12, 14, 16, 18, 20]
パイプラインの必要性がわからない。まあ、だから無くなったんだと思うけど。
後は、モジュールについて。属性の説明が面白い。