CodeZine(コードジン)

特集ページ一覧

並列処理に関数型…でも学習コストは高くない!? Web開発者のためのElixirのススメ【デブサミ2019】

【15-D-5】 これをまだ知らない Web エンジニアへ贈る - 私が愛する Elixir/Erlang の楽しさと辛さ -

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2019/04/25 12:00

 ErlangのVM上で動作する関数型言語「Elixir」。Ruby風とも言われる文法のわかりやすさと、大量の並列処理を安定的に実行できる特徴から、近年ではゲームやSNS、コンテンツ配信などのWebアプリケーション開発で採用される事例も増えている。とはいえ、RubyやPythonなどに比べるとマイナーな言語で、Elixirを扱えるWeb開発者もまだまだ少ないのが実情だ。比較的歴史の浅い言語であるうえ、「並列処理」や「関数型」といった特性に対する敷居の高さ、学習コストへの懸念から、開発者に敬遠されやすい一面もあるだろう。gumi CTOの幾田雅仁氏は、Elixirに対するそういったイメージは「誤解」であり、むしろ「学習コストのわりに恩恵が大きい言語」だと主張。その根拠を示しながら、Webシステムの開発にElixirを使うメリットや、入門者向けに最適な学習法などを紹介した。

株式会社gumi Chief Technical Officer 幾田雅仁氏
株式会社gumi Chief Technical Officer 幾田雅仁氏

並行性、メンテナンス性、対障害性に優れるが、はやっていない「Elixir」

 Web開発者の多くは、常に納期の短い仕事に追われている。なんとかサービスをリリースした後も増え続けるクライアント、複雑化するコード、肥大化するサーバー構成、障害対応のコスト増加……と、悩みは尽きない。こうしたつらい状況を改善するための3つの要素として、幾田氏は「並行性」「メンテナンス性」「耐障害性」を挙げる。

 「『Elixir』は、それら3つをすべて備えている。Web開発のための優秀なツール類も完備しており、まさにWeb開発者の悩みを解決するために存在するような言語」(幾田氏)

 もちろん、gumiでもゲームの認証/課金サービスやAPIサーバー、プロキシサーバーなどでElixirを採用しているという。

 しかし、現状は決してWeb開発の現場で引く手あまたというわけではない。幾田氏が紹介したある求人サイトの正社員求人数を言語別に比較したデータ(2019年2月時点)によると、Rubyが1万件以上、Node.jsとScalaがそれぞれ1000件以上であったのに対して、Elixirの求人数はわずか163件。むしろ「はやっていない」状況だ。

他の言語に比べてElixirの求人数はかなり少ない
他の言語に比べてElixirの求人数はかなり少ない

 なぜElixirは、はやらないのだろうか? 大きな理由として考えられるのが、「学習コストが高い」と思われていることだ。納期が短い仕事を抱える中で、学習コストが高い(と思われている)言語はなかなか採用されにくい。

 「主に『並行処理』と『関数型』といった言葉に対する重々しいイメージから、Elixirは学習コストが高いと思われがちだが、実際はそうではない。特にWebシステムの開発に関して言えば、Elixirは学習コストのわりには恩恵が大きい言語だとお伝えしたい」と幾田氏は続けた。

並行処理を意識せず、「逐次処理を書くだけ」でOK

 Elixirをおすすめする根拠として、幾田氏はまず、「並行処理」について説明した。Elixir入門者にとっては、そもそも並行処理の概念自体の学習にコストが必要なイメージがあるが、その点から否定する。

 「Elixirのフレームワーク『Phoenix』でWebサービスを開発するなら、逐次処理を書くだけで、軽量プロセスによる並行処理の恩恵を享受できる。少なくとも最初のうちから並行処理について学習する必要はなく、しばらく忘れていてもよい」

 これは、通常のOSでいう、コンテキストスイッチ等の複雑な処理を、Phoenix(Elixir)の動作環境であるEVM(Erlang VM)側で吸収してくれるからだという。

 EVMは、OSと似たプロセスの仕組みを持つ。その具体的な仕組みとして、幾田氏は次の3点を挙げて説明した。

  • 独立したCPUを持つ
  • 独立したメモリを持つ
  • ネットワーク上の一意な住所を持つ

独立したCPUを持つ

 EVMは、各軽量プロセスにCPUを使う権利を与える。軽量プロセスが一定量処理をすると、別の軽量プロセスに権限が移るようになっている。1つの軽量プロセスがCPUを専有しにくい仕組みなので、極論、軽量プロセス内で無限ループしたとしてもシステム全体に影響が出にくい。そのため、各軽量プロセス内はシンプルな逐次処理となる。

ある軽量プロセスが一定量処理すると、別の軽量プロセスにCPUを使う権利が移る
ある軽量プロセスが一定量処理すると、別の軽量プロセスにCPUを使う権利が移る

独立したメモリを持つ

 EVMは、軽量プロセスごとにメモリを確保して与える。1つのプロセスは、ヒープ、スタック、メールボックス、プロセス制御ボックスの4種類で構成されている。この仕組みにより、プロセスを終了するとメモリを解放できるので、システム全体でメモリリークが発生しにくい。また、プロセスごとにGC(ガベージコレクション)が走り、GC中にコンテキストを切り替え可能。そのため、GCでシステム全体が停止するといったことはない。

ネットワーク上の一意な住所を持つ

 EVMは、各軽量プロセスに一意なアドレス(PID)を割り当てる。軽量プロセス同士はPIDを使ってメッセージを送受信し、共有メモリを使わずに連動可能。通常は、軽量プロセス同士をリンクした状態で、片方がクラッシュしたらもう一方に終了シグナルを送信し、シグナル受信側もクラッシュすることになるが、trap exit = trueを設定してリンクすれば、終了シグナルをメッセージとして受信することにより、プロセスの再起動などを行うこともできる。

 このように、EVMの軽量プロセスは「耐障害性」を向上するのに有効な仕組みを持つ。ただし、EVM=OSであるため、OSの運用で考慮するべきことは、EVMでも考慮しなければならない点に注意したい。以下の点などだ。

  • CPUを100% 使い切るプロセスは、システム障害に発展するケースが多い
  • メモリを大量に消費するプロセスは、システム障害に発展するケースが多い
  • プロセスの起動と停止を高速に繰り返すとCPU負荷が上がり、システム障害に発展するケースが多い
  • 戦略なくプロセスを大量に起動し、それらを連携させると、システム設計が複雑になるケースが多い

 また、極端に遅いわけではないが、「速さ」はそれほどでもないようだ。純粋な1プロセスによる計算では、C、Java、Node.jsより遅く、Python、Ruby、PHPよりは速いレベルだという。

 「つまり、Elixirは『並行処理の学習コスト不要で、ほどほどに遅くない、とても楽で安全な言語』と言える」(幾田氏)

実は関数型由来の仕組みは少ない。学ぶことの取捨選択が大事

 Elixirの学習コストが高いと思われがちなもう1つの要素である「関数型」についても、「Elixirは純粋な関数型言語ではない」と、幾田氏は指摘する。

 関数型言語の純粋性の条件としては、演算子、例外、IOなどが関数で表現されていることや、副作用を起こさないことなどが挙げられる。Elixirはこれらに当てはまらず、実は関数型由来の仕組みは少ないという。

 「Elixirでも、繰り返しや条件分岐、例外処理、入出力などを使ってコードが書ける。ただ、これらを複雑に組み合わせるのはElixirのよさが損なわれるし、慣れるにしたがってシンプルに書けることがわかる」(幾田氏)

 続いて幾田氏は、Elixir入門者向けに最適な学習リソースとして次の4点を紹介。

プログラミングElixir

 Elixirらしさを学ぶのに最適な、Dave Thomas氏による解説書。Elixirの思想が詰まっている。入門者が読むべき範囲は11~157ページ。

Elixir入門

 「gumi TECH Blog」にて公開。解説の順番はElixirの公式サイトと同様。「11:プロセス」「16:プロトコル」「20:ビヘイビア」は上級者向けなので、読み飛ばしてよい。

Elixir School

 Elixir初学者用のWebサイト「Elixir School」の日本語訳。はじめは「基本」の章だけ読めばよい。

Phoenix GUIDES

 フレームワークPhoenixの解説サイト(英語)。「Up and Running」から読み始める。一般的なWebサービスの用途なら、「Channels」と「Presence」は読み飛ばしてよい。

 さらに幾田氏は以上のリソースを読む前に覚えておいてほしい要素として、リスト、Enum、パイプライン、doについて解説した。『プログラミングElixir』では、「プログラミングとはデータを変換することである」と紹介されているが、この4つはまさにデータ変換の道具だ。

リスト

 まず、[1 | 2] のようなペアという構造があり、リストとはペアで作ったリンクリスト(連結リスト)であることを理解する。[1] という1つのリストも、右が空リストのペアである [1 | []] と記述できる。3要素のリストは、[1, 2, 3] = [1 | [2 | [3 | []]]] となる。

 これを知ると、リストの性質が理解できる。例えば、リストの先頭に要素を追加する場合、末尾よりも先頭に追加するほうが高速なのは、ペアを1つ用意するだけで済むからだとわかる。この構造を前提に、再帰などについても学習していく。

リストを「ペアで作ったリンクリスト」として捉えると理解しやすい
リストを「ペアで作ったリンクリスト」として捉えると理解しやすい

Enum

 Enumモジュールの関数は、よく使うものから覚える。mapやfilterのほか、『プログラミングElixir』『Elixir School』で紹介されている関数などを参照。また、_by、_whileなどのサフィックスのルールを覚えておく。

パイプライン

 パイプラインの存在意義は可読性が上がることと、関数定義に一貫性が生じること。開発者にとって便利なので、「パイプライン演算子が使えるように関数を定義する」という習慣がつき、結果として予測のしやすい関数ができあがるようになる。

do

 doブロックを引数にとるものは、「変数のスコープと値(戻り値)を持つ」という法則を念頭に置くと、さまざまな構文を理解しやすくなる。

 「最初は学ぶことを取捨選択するのが大事。そうすることで学習コストは下げられる」と話す幾田氏。まず集中して学習すべきなのは「リストの扱い方」であり、ほかのことは「リスト処理に習熟してからゆっくり学ぶ」スタンスがよいそうだ。

 まとめとして幾田氏は、Webシステムの開発に限定すれば、Elixirの学習コストはほどほどで済むうえに、安全性および生産性の高い開発が実現できることをあらためて訴求。

 最後に、もっと理解を深めたい人向けに、「gumi TECH Drinkup」というgumi主催の技術交流イベントが度々開催されいてることや、「Erlang & Elixir Fest 2019」が6月1日に永田町 JA共済ビルで開催されることを紹介し、内容盛りだくさんのセッションを終えた。

お問い合わせ

 株式会社gumi

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • CodeZine編集部(コードジンヘンシュウブ)

    CodeZineは、株式会社翔泳社が運営するソフトウェア開発者向けのWebメディアです。「デベロッパーの成長と課題解決に貢献するメディア」をコンセプトに、現場で役立つ最新情報を日々お届けします。

All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5