Linux/Macでkerlを使用して未リリース版のErlang/OTPをお試しする（バージョン17のmap型を体験）

Erlang/OTP 17では新しいデータ型としてmap型が導入される予定だ。list型やbinary型と同様に、専用の文法を持ち、パターンマッチやmap comprehensionなどに対応している。かなり期待できる機能だ。

現時点では、まだ仕様が確定していないようだが、現在の仕様では以下のように使える。（ **2014年2月1日追記** 現状ではmapの文法サポートに制限があり、mapのキー部分に変数を使用できない。またmap comprehensionにも未対応とのこと。当分は標準ライブラリの `maps` モジュール経由のアクセスが主流になりそう）

```erlang
> M = #{ key1 => 100, {key2, 1.0} => "yo!"}.
> M1 = M#{ key1 := 101 }.
> M2 = M1#{ fun1 => fun(X) -> X end }.
> F1 = M2#{ fun1 }.    %% 単一の値の取り出し。まだ動作しない。
> #{ key1 := V1, {key2, 1.0} := V2 } = M2.  %% パターンマッチ
> V1.
101
> V2.
"yo!"
```

バージョン17はまだリリースされていないので、map型を試すためには、GitHubから開発版のソースコードをダウンロードして、自分でビルドする必要がある。そこで、kerlの実験的な機能を使用して、未リリース版のErlang/OTPを導入してみることにした。

kerlは複数バージョンのErlang/OTPを簡単に導入・管理するためのスクリプトだ。本来はリリース版のみを対象にしたツールだが、実験的な機能として、gitリポジトリからソースコードをダウンロードして、ビルド、インストールすることもできる。（以下、Erlang/OTPを、Erlangと記述）

# 準備

まず、こちらの記事を参考に、kerlスクリプトをインストールする。

- [Linuxでkerlを使用して複数バーションのErlang/OTPを導入する](http://qiita.com/tatsuya6502/items/f15da8ea6e793c5038a2)
- [Macでkerlを使用して複数バーションのErlang/OTPを導入する](http://qiita.com/tatsuya6502/items/a356db103f3b654d6f27)

さらに、gitもインストールしておく。手順は、ネットで検索すればすぐにわかるので、省略する。

# 未リリース版のErlang/OTPのビルド

未リリース版のErlangのソースコードをgitリポジトリからダウンロードして、ビルドする。当たり前のことだが、未リリース版は仕様が変更されたり、一部の機能が動作しなかったり、致命的なバグが含まれていたりするので、本番環境で使用してはいけない。あくまでもお試し版として触ってみる程度に留めておく。

もちろん、バグを見つけて報告したり、パッチを送ったりして、開発コミュニティに貢献することもできる。

## ソースコードからビルド

kerlでgitにあるソースコードからErlangをビルドするには、以下のようにする。

```bash
$ kerl build git <gitリポジトリのURL> <ブランチ または タグ> <ビルドにつける任意の名前>
```

バージョン17のmap型は[GitHub erlang/optのmasterブランチ](https://github.com/erlang/otp/tree/master)にある。以下のコマンドでビルドした。

```bash
$ KERL_CONFIGURE_OPTIONS="--disable-hipe --enable-smp-support --enable-threads --enable-kernel-poll" kerl build git https://github.com/erlang/otp.git master r17rc1_20140201
```

ちなみに環境は Arch Linux。

```bash
$ uname -a
Linux arch-mini 3.12.9-1-ARCH #1 SMP PREEMPT Sun Jan 26 09:01:37 CET 2014 x86_64 GNU/Linux
```

（ **2014年2月1日追記** Erlang/OTP 17については、erlang.orgからもリリース候補版 RC1 のソースコードがダウンロードができるようになったようだ。バージョンナンバーの振り方に変更があり、kerlでビルドするときは最新版のkerlが必要となる（[参考](https://twitter.com/jj1bdx/status/429457344754245632)）。[最新版を取得](https://github.com/spawngrid/kerl#downloading)後に `kerl update releases`を実行すると、リストの先頭に `17.0-rc1` と表示される。）

## インストール

無事ビルドできたらインストールする。

```bash
$ kerl install r17rc1_20140201 ~/erlang/r17rc1_20140201
```

# Erlang/OTP 17のMap型を試してみる

```erl
$ . ~/erlang/r17rc1_20140201/activate
$ erl
Erlang/OTP 17 [RELEASE CANDIDATE 1] [erts-6.0] [source-eec1d22] [64-bit] [smp:8:8] [async-threads:10] [kernel-poll:false]

Eshell V6.0  (abort with ^G)
1>
```

Map型の仕様はこちらにある：[EEP 43](https://github.com/erlang/eep/blob/master/eeps/eep-0043.md)。また、書籍 [Programming Erlang, Second Edition (2013年、Joe Armstrong著)](http://pragprog.com/book/jaerlang2/programming-erlang) でも紹介されている。まずは書籍の例を拝借。

```erlang
> Facts = #{ {wife, fred} => "Sue", {age, fred} => 45, {daughter, fred} => "Mary" }.
#{{age,fred} => 45,{daughter,fred} => "Mary",{wife,fred} => "Sue"}
```

パターンマッチ

```erlang
> #{ {age, fred} := FredsAge, {wife,fred} := FredsWife } = Facts.
#{{age,fred} => 45,{daughter,fred} => "Mary",{wife,fred} => "Sue"}
> {FredsAge, FredsWife}.
{45,"Sue"}
```

[EEP 43](https://github.com/erlang/eep/blob/master/eeps/eep-0043.md)より、パターンマッチを使用した、リスト内の単語の出現頻度のカウントプログラム。（コンパイルできなかった → バージョン17ではmapの構文サポートに制限がつく見込み。後述の2014年2月1日追記を参照）

```erlang
freq_m(Is) -> freq_m0(Is, #{}).

freq_m0([I|Is], #{I := C} = M) -> freq_m0(Is, M#{ I := C + 1 });
freq_m0([I|Is], M)             -> freq_m0(Is, M#{ I => 1 });
freq_m0([], M)                 -> maps:to_list(M).
```

従来の方法。gb_treesを使用する場合。

```erlang
freq_t(Is) -> freq_t0(Is, gb_trees:empty()).

freq_t0([I|Is], T) ->
    case gb_trees:lookup(I, T) of
        none       -> freq_t0(Is, gb_trees:enter(I, 1), T);
        {value, V} -> freq_t0(Is, gb_trees:enter(I, V + 1, T))
    end;
freq_t0([], T) -> gb_trees:to_list(T).
```

後でもう少し試して、ここに追記する予定。とりあえず、Erlangランタイム環境のテストケースが、今は一番参考になるかも。[erts/emulator/test/map_SUITE.erl](https://github.com/erlang/otp/blob/master/erts/emulator/test/map_SUITE.erl)


---------------------------------
**2014年2月1日追記** 現状はmapの文法サポートに制限があり、mapのキー部分に変数を書いたり、単一のキー指定での値の取り出しはできないとのこと。また map comprehension もサポートしない（[erlang-questions: Maps branch and disclaimers](http://erlang.org/pipermail/erlang-questions/2013-October/075752.html)）　そのため、代わりに標準ライブラリの `maps` モジュールを使うことになる：

例：

- `M#{ K }` の代わりに `maps:get(K :: term(), M :: map()) -> V :: term()` を使用。
- map comprehension `#{ 式1 => 式2 || K := V <- Map }` の代わりに、`lists:foldl/3` を使用。

```erlang
lists:foldl(fun({K, V}, M) -> maps:put(式1, 式2, M) end, #{}, maps:to_list(Map))
```