君もelixirを始めてみないか

elixirって何?

elixir (エリクサー)はErlangで作られた言語だ。
最初はCoffeeScriptとJavaScriptみたいな関係なのかと思ってくれれば良い。
Erlangの上に作られているから、elixirの特徴を語るにはまずErlangについて述べる必要がある。

Erlangは1986年にJoe Armstrong, Robert Virding, Mike Williams の3人によって最初のバージョンが作られた。
元々は通信機器メーカーのEricsson内部の独自言語だったけれど、1998年にオープンソースとしてリリースされた。
Erlangの特徴をぎゅっとまとめると、並行性と信頼性とホットスワップだ。
Erlangの開発は Joe Armstrong 博士が電話交換制御用ソフトウェアを構築するために、Prologをベースに並行プロセスやエラー処理の仕組みを追加したことに端を発している。
並行性と信頼性、ホットスワップの特徴は通信事業者向けの分散アプリケーションの開発に必要だったんだ。

elixirはBEAMと呼ばれるErlangの仮想環境で動作するから、Erlangの並行性と信頼性の特徴を受け継いでいる。
そして拡張機能としてマクロを使ったメタプログラミング等も扱うことが出来る。

elixirよりまず、Erlangの知名度も高くは無いよね。
でも案外身近なところで使われてたりするんだ。
TwitterではejabberdというJabber/XMPP実装が使われている(今現在もそうかは分からないけど)ようだし、WhatsAppもErlangを使用しているみたいだ
Erlangは何億人とユーザーを抱えるような大規模なシステムでかなりの実績を積んでいる。
プロセッサのマルチコア化が進む現在、並行性と信頼性を備えるErlangが活躍できる場面が多くなっていくんじゃないかなって思わないかい?
Matzも注目する言語なんて記事もあるよ。

そんなに言うならelixirじゃなくて素のErlangで良いじゃんって思うかもしれない。
答える代わりに質問だ、上の記事は2007の物なんだけど、Erlangって今流行ってるだろうか?
ちょっとこの記事を見て欲しい"Damien Katz氏がApache CouchDBから離反し、Couchbase Server開発を継続“CouchDBがコードの大半をErlangからC/C++するって内容だ。
Damien Katz氏はインタビューの中で流行ってない原因を述べている。

Erlangは素晴らしい言語です。信頼性が高く、信頼できるしっかりとしたシステムを簡単に構築できます。しかし、エコシステムがとても小さいです。なので、ツールや性能に対する投資は他の一般的な言語とは比べようもないくらい少ないです。私はErlangにもっと人気の言語になって欲しいと思います。ErlangがJavaよりも速くならない理由はどこにもありません。しかし、その奇妙な構文が人々を遠ざけ、普及と商用投資を妨げています。でも私はErlangが好きです。性能が重要なコンポーネントでの利用は少なくしますが、今後も致命的に重要なコンポーネントに使うつもりです。

そう、流行らないのは取っつきにくいからだ。
であれば、取っつきやすければ Erlang のパワーを享受できるようになる。
そういう考えで始まったプロジェクトにはReia Programming Languageという物もある、しかし残念ながらReiaの開発は停止してしまった。
Reiaの後に登場し、後継のプロジェクトに指名され、2014年9月18日にv1.0.0がリリースされたのがelixirなんだ。

開発環境は整ってるの?

何が取っつきやすくなったかって、それはもちろん文法なんだけど、今や言語そのものが良くたって開発環境が良くないと流行らないよね。
だから文法の前に開発の環境について紹介しておくね。
僕は普段はrubyを使っているから、rubyの環境と対比させて紹介するね。

まずは Mix だ。
Mix は ruby だと rakebundler を合わせたようなものだ。
rubyのプロジェクト開始は bundle init して、Gemfile を編集して、bundle installするよね。

$ mkdir ruby_app
$ cd ruby_app
$ bundle init
$ vim Gemfile
source "https://rubygems.org"

gem "rails"
$ bundle install

elixirmix の場合は mix new elixir_app して mix.exsdeps を編集して、mix deps.get だ。
ここでは depselixir-lang/ecto というライブラリを追加している。

$ mix new elixir_app
$ cd elixir_app
$ vim mix.exs
defmodule ElixirApp.Mixfile do
  use Mix.Project

  def project do
    [app: :elixir_app,
     version: "0.0.1",
     elixir: "~> 1.0",
     deps: deps]
  end

  # Configuration for the OTP application
  #
  # Type `mix help compile.app` for more information
  def application do
    [applications: [:logger]]
  end

  # Dependencies can be Hex packages:
  #
  #   {:mydep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"}
  #
  # Type `mix help deps` for more examples and options
  defp deps do
    [
      {:ecto, "~> 0.4"}
    ]
  end
end
$ mix deps.get

rubyでのRubyGems.orgに対応するライブラリのホスティングはHexという物があるし、
依存関係には GitHub のリポジトリを指定することも出来る。

インタラクティブシェルは irb に相当する iex が用意されているし、テストも標準で ExUnit という物がある。
さらに、是非紹介しておきたいのが Elixir Release Manager bitwalker/exrmだ。
詳しい使い方は後に回すけれど、exrm はリリース用のビルドを行うことが出来るし、さらにダウンタイムなしのアップグレードやダウングレードを簡単にしてくれる。

機能 ruby elixir
ビルドツール rake mix
ライブラリ管理 bundle mix
標準リポジトリ RubyGems.org Hex
インタラクティブシェル irb iex
テスト minitest ExUnit

エディターのサポートは、vim(vim elixir-lang/vim-elixir)と emacs(elixir-lang/emacs-elixir)とAtom(lucasmazza/language-elixir)があるから大丈夫かな?(他にもgeditとTextmateも用意されているよ)

はじめてみよう

始めて見るにはまずelixirをインストールしないとね。
もしMacを使っていて、すでにbrewを使っているのであればとても簡単

$ brew update
$ brew install elixir

Ubuntu を使っているなら、リポジトリを追加してapg-getで入る

$ wget http://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb
$ sudo apt-get update
$ sudo apt-get install elixir

Windowsの場合はインストーラーが用意されているよ。

これで基本的にはErlangも一緒に入っている。
早速使い始めてみよう。iexコマンドでインタラクティブシェルが立ち上がる

Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir (1.0.2) - press Ctrl+C to exit (type h() ENTER for help)
iex>

終了したいときはCtrl+Cして(a)bortaをタイプしてエンターだ。

実はErlang(erlで起動するよ)だと式の最後に.ピリオドを付けなきゃならない

Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V6.3  (abort with ^G)
1> 1+2.
3

でもelixirならピリオドはいらない、そのまま1+2とタイプしてエンターで計算できる。

iex> 1+2
3

このまま進む前に、elixirのインストールによって入った他のコマンドも紹介しておこう。
elixirelixircだ。
elixirコマンドはelixirスクリプトを実行する物だ。
ruby hoge.rbみたいな感じで、elixir hoge.exsと使用する。
elixircはコンパイル用のコマンドで、elixirc hoge.exとするとコンパイルされた.beamファイルが生成される。
ファイルの拡張子を.exs.exを登場させたけれど、
.exsはコンパイルしないで実行するスクリプトファイル、
.exはコンパイルするファイルに付けるのが習慣なんだ。

他には今後よく使う表記について説明しておこう。
elixirでは関数は"モジュール名”, “関数名”, “アリティ"(つまり引数の個数)によって一意に定まる。
だから関数を表現するときに モジュール.関数/アリティといった具合に表記する。
例えばIO.puts/1とかね。
これはiexからドキュメントを見るときにも使うよ。

iex> h(IO.puts/1)

                def puts(device \\ :erlang.group_leader(), item)

Writes the argument to the device, similar to write/2, but adds a newline at
the end. The argument is expected to be a chardata.

まずは基本的な型だ

整数、浮動小数、文字列

基本的な型を確認してみよう

iex> 1+2
3
iex> 1+2.0
3.0
iex> "string"
"string"
iex> 1 + "string"
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.+(1, "string")

整数、浮動小数点、文字列が使えることが分かった。
整数と浮動小数点の計算ではエラーにならずに自動的に型の変換もしてくれる。
暗黙に変換できな場合はもちろんエラーになる。
ちなみにelixirの浮動小数点は64ビットの倍精度だ。
四則演算は当然出来るんだけど、ちょっと注意なのが/による除算だ。
irbで10 / 3とやれば3が出力されるけど、iexなら浮動小数点で返る。
整数の商が欲しければdiv関数が用意されている。
rubyと同じで自明なカッコは省略も出来る。

iex> 10 / 3
3.3333333333333335
iex> div(10, 3)
3
iex> div 10, 3
3

二進数、八進数、十六進数、そして指数表記もサポートしている。

iex> 0b11111011111
2015
iex> 0o77
63
iex> 0xff
255
iex> 1.0e-10
1.0e-10

真偽値

真偽値はtrue,falseだ。

iex> true
true
iex> false
false

真偽値では無いけれどnilも用意されている

iex> nil
nil

論理演算はand,or,notだ。

iex> true and false
false
iex> true or false
true
iex> not false
true

論理演算には&&,||,!も用意されている。
違いは真偽値だけ受け付けるか、任意の型を受け付けるかだ。

iex> 1 and true
** (ArgumentError) argument error: 1

iex> 1 && true
true

&&,||,!の場合、falsenil以外を真として評価する。
rubyと一緒だね。

iex> not 1
** (ArgumentError) argument error
    :erlang.not(1)
iex> ! 1
false

or, and, ||, &&は短絡演算子、つまり左辺が条件を満たさない場合は右辺は評価されない。

iex> x
** (RuntimeError) undefined function: x/0

iex> true or x
true
iex> true || x
true

ここまで紹介してきた型はis_integer/1, is_float/1, is_number/1, is_boolean/1 で判定できるよ。
is_number/1は整数もしくは浮動小数点の場合にtrueだね。

iex> is_number(1)
true
iex> is_number(1.0)
true
iex> is_number(true)
false

アトム

次はアトムだ。アトムは:から始まるもので、rubyだとシンボルって呼ばれているね。
真偽値のtrue, falseは実はアトムなんだ。

iex> true == :true
true
iex> is_atom(false)
true
iex> is_boolean(:false)
true
iex> is_atom(:hoge)
true
iex> is_boolean(:hoge)
false

文字列の式展開 / 引用符による違い

文字列は二重引用符で括って使う。
文字列の結合は+ではなく<>だ。
rubyと同様に#{}によって式を展開することも出来る。
単一引用符を使おうとすると、少し驚く結果となるだろう。

iex> "foo" + "bar"
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.+("foo", "bar")
iex> "foo" <> "bar"
"foobar"
iex> "1 + 2 = #{1+2}"
"1 + 2 = 3"
iex '日本語'
[26085, 26412, 35486]
iex> "日本語"
"日本語"

これは文字列と文字のリストの違いが原因なんだ。

リストとタプル

単一引用符を使ったときに現れたものはリストなんだ。
リストは[]を使って表現する。リストには何でも入れられる

iex> [1, true, :atom, "string", [1,2]]
[1, true, :atom, "string", [1, 2]]

リストと似ているタプルという物もある。
タプルもリストと同じように何でも入れられる

iex> {1, true, :atom, "string", [1,2], {:x, :y}}
{1, true, :atom, "string", [1, 2], {:x, :y}}

似ているけれど、中身は違う。
それはリストは中身が連結リスト(Linked list)であるのに対し、タプルは要素を連続したメモリ上に保存している点だ。
タプルは連続したメモリ上に要素を持っているから、インデックスによる要素の取得や、要素数の取得は高速に出来る。
対してリストは連結リストであるため、要素を順にたどる必要があり、タプルよりは低速になってしまう。
リストの方が使い勝手が良い場合は要素を更新したり追加する時だ。
elixrのデータ型は変更不可能なため、タプルの要素の更新はタプル全体をコピーしてから行う必要がある。
リストの場合は途中の連結を変更するだけなので、タプルより簡単だ。
リストとタプル、似てはいるけれど、適切に使い分けた方が良い。
つまり変更されないような場合はタプル、変更される場合はリストといった具合だ。

リストとタプルについての操作も見てみよう。
リストは++/2, --/2で連結や、差し引いたりが出来るし、hd/1, tl/1でリストの先頭とそれ以外の取得が出来る(LISPのcar, cdrみたいだね)。
長さの取得はlength/1を使う

iex> [1,2,3] ++ [3,4,5]
[1, 2, 3, 3, 4, 5]
iex> [1,2,3] -- [3,4,5]
[1, 2]
iex> hd [1,2,3,4,5]
1
iex> tl [1,2,3,4,5]
[2, 3, 4, 5]
length [1,2,3]
3

タプルのインデックスによる要素取得、サイズ取得、要素の変更はそれぞれelem/2, tuple_size/1, put_elem/3を使う。

iex> elem({1,2,3}, 1)
2
iex> tuple_size({1,2,3})
3
iex> put_elem({1,2,3},2,4)
{1, 2, 4}

リストの要素数取得がlengthで、タプルの要素数取得がsizeなのはちゃんと理由があるんだ。
elixirではデータ構造が持っている要素数を数える場合、
値があらかじめ計算されている場合は関数名にsizeを、明示的に計算が必要なものは関数名にlengthが使われている。
自分で関数を作る場合にも気をつけてみよう。

比較演算

比較演算子には==!====!==<=>=<, >が用意されている。
基本的には想像通りの動作をする。

iex> 1 == 1
true
iex> 1 != 1
false
iex> 2 <= 2
true
iex> 2 < 2
false

=====の違いは===の方が整数と浮動小数点数を厳密に比較する点だ。

iex> 1 == 1.0
true
iex> 1 === 1.0
false

おもしろいのは異なる型の比較が出来る点だ。

iex> 1 < :x
true

これは利便性のためで、以下のような型の比較ルールが用意されている。

number < atom < reference < functions < port < pid < tuple < maps < list < bitstring

パターンマッチング

今までのコードの中で、プログラムではよく使うのに、あえて登場させなかった演算子がある。=だ。
使うのは簡単

iex> x = 1
1
iex> x
1

結果も至極当然に見える。でも、次の例はどうだろうか

iex> 1 = x
1

もしこれをirbで動かしていればシンタックスエラーだ。

irb(main)> x = 1
=> 1
irb(main)> 1 = x
SyntaxError: (irb):3: syntax error, unexpected '=', expecting end-of-input
1 = x
   ^

でもelixirでは1 = xはエラーにはならなかった。
実はelixirの(もしくはErlangの)=は代入の演算子ではなく、パターンマッチングを行うマッチ演算子なんだ。
1 = xはパターンマッチングとして成立しているからエラーにはならない。
2 = xはマッチしないからエラーとなる。

iex> 2 = x
** (MatchError) no match of right hand side value: 1

マッチ演算子の左辺に変数があり、パターンマッチングが成立すれば変数に割り当てがされる。

iex> x = 2
2
iex> x
2

当然じゃ無いか、これなら代入と同じだと思うかもしれない、でもErlangよりはとっつきやすい。
もしErlangで同じ事を実行したなら

注: Erlangの変数は大文字で始まる(Prologみたいだね)、そして式の最後にはピリオドがいる

Eshell V6.3  (abort with ^G)
> X = 1.
1
> X.
1
> 1 = X.
1
> 2 = X.
** exception error: no match of right hand side value 1
> X = 2.
** exception error: no match of right hand side value 2
> X.
1

そうErlangではX = 2でもエラーとなる。
全ての変数はイミュータブルであり、代入は1回だけに限られている。
不便に思えるかもしれないけれど、関数型言語の考えを使うなら便利にもなる。
elixirではピン演算子^によって再束縛をコントロールすることが出来る。

iex> ^x = 3
** (MatchError) no match of right hand side value: 3
iex> x
2

徐々にパターンマッチングが代入と異なる点に踏み込んでみよう。
マッチ演算子はリストやタプルの構造でも使うことが出来る。

iex> {x, y, z} = {1, :hoge, "bar"}
{1, :hoge, "bar"}
iex> x
1
iex> y
:hoge
iex> z
"bar"

これだけだと一気に代入したのと変わらないように見えるけれど、左辺に変数以外があったらどうなるだろう。

iex> {:name, x} = {:name, "taro"}
{:name, "taro"}
iex> x
"taro"
iex> {:name, x} = {:age, 1}
** (MatchError) no match of right hand side value: {:age, 1}

この例だとタプルの先頭要素が:nameの時のみ、変数xに割り当てが出来ると言うことだ。
左辺に同じ変数を複数回使ったなら、同じものに束縛できなければパターンマッチングは成功しない。

iex> {x, y, x} = {1, 2, 3}
** (MatchError) no match of right hand side value: {1, 2, 3}
iex> {x, y, x} = {1, 2, 1}
{1, 2, 1}

ピン演算子^は以前に束縛した値とのマッチングを表現できる。

iex> x = 1
1
iex> {x, x} = {2, 1}
** (MatchError) no match of right hand side value: {2, 1}
iex> {x, ^x} = {1, 1}
{1, 1}
iex> {x, ^x} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}
iex> {x, ^x} = {2, 1}
{2, 1}
iex> x
2

パターンマッチングにおいて特殊な変数に_がある。
これは簡単に言えばなんでもいいってことだ。

iex> {y, y, x} = {1, 2, 3}
** (MatchError) no match of right hand side value: {1, 2, 3}
iex> {_, _, x} = {1, 2, 3}
{1, 2, 3}
iex> x
3

リストのパターンマッチング

タプルと同じようにリストだってパターンマッチング出来る。

iex> [x, y, z] = [1, :hoge, "bar"]
[1, :hoge, "bar"]

リストの中身は連結リストだという話をしたよね、だから「先頭要素」と「続くリスト」というマッチングも出来る。

iex> [head | tail] = [1, :hoge, "bar"]
[1, :hoge, "bar"]
iex> head
1
iex> tail
[:hoge, "bar"]

[head | tail]と指定したなら、先頭も続きも無い空のリストとはマッチングしない

iex> [head | tail] = [1]
[1]
iex> tail
[]
iex> [head | tail] = []
** (MatchError) no match of right hand side value: []

[head | tail]の書き方は実はマッチング以外にもリストそのものの表記としても使うことができる。
簡単な例は先頭への要素の追加だ。

iex> list = [1,2,3]
[1, 2, 3]
iex> [0 | list]
[0, 1, 2, 3]

[1 | [2]][1 | 2]は意味が違ってしまうから気をつけよう

iex> [1 | [2]]
[1, 2]
iex> [1 | 2]
[1 | 2]

キーワードリストとマップ

リストは登場したけれど、まだ連想配列(RubyだとHash)を紹介していなかったね。
elixirではキーワードリストとマップと呼ばれる2種類の連想データ構造があるんだ。

キーワードリスト

まずはキーワードリストだ。
elixirでは,「最初の要素がアトムとなっているタプル」のリストをキーワードリストと呼ぶ。

iex> k_list = [{:x, 20}, {:y, 30}]
[x: 20, y: 30]

キーワードリストを使うとき、省略した記法もサポートされている。

iex> k_list = [x: 20, y: 30]
[x: 20, y: 30]

要素へのアクセスも出来る。指定のキーが無い場合はnilになる。
rubyのHashと同じような使い勝手だね。

iex> k_list[:x]
20
iex> k_list[:z]
nil

キーワードリストはあくまで「タプルのリスト」だから、操作もリストと同じだ。

iex> k_list ++ [z: 40]
[x: 20, y: 30, z: 40]
iex> [{:z, 40} | k_list]
[z: 40, x: 20, y: 30]

あくまでリストだから、キーの順番は保たれるし、同じキーが複数回あってもいい。
キーによるアクセス時、同一のキーがキーワードリスト内部にあっても、前方の値が優先される。

iex> new_list = k_list ++ [z: 40, x: 10]
[x: 20, y: 30, z: 40, x: 10]
iex> new_list[:x]
20

マップ

マップは%{}を使うと定義することができる。

iex> %{:x => 20, :y => 30}
%{x: 20, y: 30}

キーワードリストでは最初の要素がアトムだったがm、マップではキーをどんな値にもする事ができる。
キーワードリストはキーの順序が保たれ、複数の同一キーも許された、
しかしマップではキーの順序は保たれず、もしマップを作るときに同じキーが渡されると最後の一つが優先される。

iex> %{:x => 20, :x => 30}
%{x: 30}

マップのキーが全てアトムの場合はrubyのhash記法のように省略を使うことが出来る。

iex> %{x: 20, y: 30}
%{x: 20, y: 30}

マップは与えられたマップのキーがある部分にだけマッチするので、パターンマッチングに使いやすい。

iex> %{name: n, age: x} = %{name: 'taro', age: 30}
%{age: 30, name: 'taro'}
iex> n
'taro'
iex> x
30
iex> %{name: n} = %{name: 'taro', age: 30}
%{age: 30, name: 'taro'}
iex> n
'taro'
iex> %{name: n} = %{age: 30}
** (MatchError) no match of right hand side value: %{age: 30}

マップにはキーにアクセスするための構文が提供されている。

# 要素へのアクセス
iex> map = %{name: 'taro', age: 30}
%{age: 30, name: 'taro'}
iex> map.name
'taro'
iex> map.age
30
iex> map.x
** (KeyError) key :x not found in: %{age: 30, name: 'taro'}

# 要素の更新
iex> %{map | name: 'ziro'}
%{age: 30, name: 'ziro'}
iex> map
%{age: 30, name: 'taro'}
iex> %{map | x: 'xxx'}
** (ArgumentError) argument error
    (stdlib) :maps.update(:x, 'xxx', %{age: 30, name: 'taro'})
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1261: :lists.foldl/3

まだ追記していくよ