adingoアルバイターの @mururururu です。
主にFluctというSSPのRTB周りを担当しています。
FluctのRTBサーバは Erlang で書かれており、個人的にも Erlang 周りの技術に注目しています。
今回はあえて Erlang ではなくErlangVM 上で動くプログラミング言語 Elixirについて紹介してみます。
Elixirは Clojure, Ruby などの言語から強い影響を受けている動的型付き(関数型)言語です。JVM上で動くScalaがJavaの資産を活用できるように、ElixirもErlangの軽量プロセスを用いた並列処理、耐障害性などの特徴をフルに利用することが出来ます。
そんなElixirの魅力的なところを少し紹介してみます。
パターンマッチ
関数型言語によくあるやつですね。
データ構造から情報を抽出する機能とでも言うのでしょうか。実際の例を見た方が早いと思います。
{ x, y } = { 1, 2 }x #=> 1y #=> 2
左辺が右辺にマッチするように変数に値が束縛されます。
また、関数の引数でパターンマッチしマッチした節を実行する、というような使い方もします。
defmodule Fib dodef fib(0), do: 1def fib(1), do: 1def fib(n), do: fib(n-1) + fib(n-2)endFib.fib(5) #=> 8
フィボナッチ数列です。if文などは一切出てきません。
そして個人的にはかなり便利だと思っているバイナリのパターンマッチです。対象の長さ、エンディアン、また数値型として取り出すかバイナリ型として取り出すかなど細かく指定できます。バイナリプロトコルのパースなどがかなり簡潔に書けます。
例えば、先頭の符号なし16ビット整数が取り出したいデータの長さ(オクテット長)を表し、その後に目的のデータが続くようなバイナリから、そのデータを取り出す関数は以下のような感じになります。
def parse(<< length :: [size(16), big, unsigned, integer],data :: [size(length), binary, unit(8)],rest :: binary >>){ data, rest }end
引数でパターンマッチするだけで終わってしまいます。パターンマッチの強力さが少しは伝わったでしょうか。
軽量プロセス
Elixirの特徴といえばなんといっても軽量プロセスです。軽量プロセスはErlangVM上でそれぞれ独立して動き、データを共有しません。GCもプロセスごとに行われます。プロセス同士のやりとりは基本的には非同期なメッセージングのみです。それによりErlangVMはそれぞれのプロセスの実行を勝手にマルチコアに分散することができます。
多くのElixirのアプリケーションではそういった多数の独立した軽量プロセスがメッセージをやりとりしながら協調する動作モデルとなっています。
以下は map関数を並列に行う例です。
defmodule Parallel dodef pmap(collection, fun) dome = self()collection|> Enum.map(fn(elem) ->spawn_link(fn -> (me <- { self(), fun.(elem) }) end)end)|> Enum.map(fn(pid) ->receive do{ ^pid, result } -> resultendend)endendParallel.pmap(1..10000 fn(x) -> x + 1 end)#=> [2, 3, 4, …]
文法を説明せずにこれを解説するのはちょっとつらいのですが、かいつまんで説明を試みてみます。まず、
Enum.map(fn(elem) ->spawn_link(fn -> (me <- { self, fun.(elem) }) end)end)
spawn_linkは新たなプロセスを生成し引数で渡した無名関数をそのプロセスで実行させる関数です。ここでは collection の各要素それぞれに対してプロセスを生成し、そこで実際の処理を行わせ、その結果を"<-"演算子で生成元のプロセスにメッセージとして送信しています。
Enum.map(fn(pid) ->receive do{ ^pid, result } -> resultendend)
ここでは各要素に対してメッセージを待ち受け、結果を受け取る処理をしています。
この関数は実際にマルチコアで実行されます。
マクロ
Lispとかにあるあれです。
ElixirのプログラムのASTはElixirのデータ構造を用いて表現されており、Elixirのプログラムは自身のASTを操作することができます。
実際にどういった場面で使われているかを標準添付のユニットテストライブラリであるExUnitを例に見てみます。
以下はExUnitのアサーションです。
assert 2 > 3
これは正しくないので、もちろんテストは失敗します。
実際にこれを実行してみると、
** (ExUnit.ExpectationError)expected: 2to be more than: 3
というエラーメッセージが表示されます。
もしassertがただの関数であるならば "2 > 3" の結果、つまり false が assert に引数として渡されます。そうするとエラーメッセージにこのような詳細な出力をすることはできません。
実際には assert はマクロとして実装されており、"2 > 3" の ASTである {:>, [], [2, 3]} を受け取りそれを操作することにより適切なエラーメッセージを出すことができています。
まとめ
いかかでしょうか。Elixirの楽しげなところが少しでも伝わったらうれしいです。
かなりErlangに寄り添っているのでErlangを書いてる人から見れば何をいまさら、という感もありますが、細かいところの使い勝手はなかなかよくなっていて個人的にはとても気に入っています。
最近では、オライリーから本が出版されたり、海外で実際にプロダクション環境で運用されるケースも出てきていたりと盛り上がり始めているようです。
かなりおもしろい言語ですのでぜひ触ってみてください。
ではでは。
adingoでは一緒に働いてくださる仲間を募集しています。
VOYAGE GROUPや広告配信に興味を持たれましたら、気軽にご連絡ください。
http://voyagegroup.com/crew/recruit/ 
