What's Coming in Elixir 1.4


この記事は[Elixir (その2)とPhoenix Advent Calendar 2016](http://qiita.com/advent-calendar/2016/elixir2_and_phoenix) 12日目の記事です。

Elixir 1.3のリリースからはや６ヶ月、Elixir v.1.1より大体６ヶ月ごとにマイナーリリースをしているようで、順調にいけば年明けにはv.1.4の正式版がリリースされそうです。

ということで来るべき[Elixir 1.4](https://github.com/elixir-lang/elixir/blob/v1.4/CHANGELOG.md)で新しく追加される機能の一部を紹介します。

## Registry

v1.4で追加されたモジュールです。

公式の説明によると

> The registry is a local, decentralized and scalable key-value process storage:

RegistryはKey-Value型で分散型のスケーラブルなローカルストレージです。

> - Local because keys and values are only accessible to the current node (opposite to distributed)

ローカルなので現在動作中のノードからのみアクセス出来ます。

> - Decentralized because there is no single entity responsible for managing the registry

Registryを管理する単一のエンティティが存在しないため分散しています

> - Scalable because performance scales linearly with the addition of more cores upon partitioning

パーティショニング時にコアが追加されるため、パフォーマンスが直線的に増加するためスケーラブルです

> A registry is chosen upon start to have unique or duplicate keys. Every key-value pair is associated to the process registering the key. Keys are automatically removed once the owner process terminates.

一意のキーや重複するキーを持つようにレジストリが選択されました。すべてのキーと値のペアは、キーを登録するプロセスに関連付けられます。オーナープロセスがterminateされるとキーは自動的に削除されます。

```elixir
iex> Registry.start_link(:unique, MyRegistry)
iex> {:ok, _} = Registry.register(MyRegistry, "hello", 1)
iex> Registry.lookup(MyRegistry, "hello")
[{self(), 1}]
```

>With the registry, developers can provide dynamic process names, module-function dispatch or even a local pubsub system. See the Registry documentation for more information.

Registryによって動的なプロセス名や特定モジュールのfunctionをdipsatchしたりlocalなpubsubを実装出来ます。

とのことです。
[ドキュメント](http://elixir-lang.org/docs/master/elixir/Registry.html)にDispatcherやPubSubの説明の説明が書いてあったので参考に動かしてみます。

- Dispatcher

```elixir
/ # iex
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.4.0-rc.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> {:ok, _} = Registry.start_link(:duplicate, Registry.DispatcherTest)
{:ok, #PID<0.82.0>}
iex(2)> {:ok, _} = Registry.register(Registry.DispatcherTest, "hello", {IO, :inspect})
{:ok, #PID<0.83.0>}
iex(3)> Registry.dispatch(Registry.DispatcherTest, "hello", fn entries ->
...(3)>   for {pid, {module, function}} <- entries, do: apply(module, function, [pid])
...(3)> end)
#PID<0.80.0>
:ok
```

`apply/3`を使って`hello`というtopicに対して登録されたプロセスで`IO.inspect`を実行しています。

- local PubSub

```elixir
/ # iex
Erlang/OTP 19 [erts-8.1] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

Interactive Elixir (1.4.0-rc.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> {:ok, _} = Registry.start_link(:duplicate, Registry.PubSubTest,
...(1)>                                partitions: System.schedulers_online)
{:ok, #PID<0.83.0>}
iex(2)> {:ok, _} = Registry.register(Registry.PubSubTest, "hello", [])
{:ok, #PID<0.85.0>}
iex(3)> Registry.dispatch(Registry.PubSubTest, "hello", fn entries ->
...(3)>   for {pid, _} <- entries, do: send(pid, {:broadcast, "world"})
...(3)> end)
:ok
```

Registry経由で特定のトピック(`hello`)をsubscribeしてるプロセスに大して`{:broadcast, "world"}`というようなメッセージを`send/2`でbroadcastしています。

Erlangでいう[gproc](https://github.com/uwiger/gproc)のようなことが出来るModuleみたいです。gprocと同様にETSを使って実現し、ETSテーブルのshardingなどで性能を高めているそうです。
[gprocと同様に](http://blog.rusty.io/2009/09/16/g-proc-erlang-global-process-registry/)複数のプロセスに同じ名前を付けたり一つのプロセスに複数の名前を登録出来るのでPubSubやDispatcherとしてEvent Handlingなど汎用的に使えます。

[Elixirでgprocを使う](https://m.alphasights.com/process-registry-in-elixir-a-practical-example-4500ee7c0dcc#.7hc33zw2v)例はあるけどインターフェースがErlangになるので標準でモジュールを提供してくれるのはありがたいですね。
Event HandlingするのにはGenEventというモジュールが既にあるけど[GenEvent特有の問題](http://www.afronski.pl/2015/11/02/what-is-wrong-with-gen-event.html)もあるみたいで代わりとしてこれからRegistryを使っていけそうです。

## Syntax coloring

ANSI Syntax Highrightがiexに来ました。

![image](https://qiita-image-store.s3.amazonaws.com/0/47983/92bb393e-2b65-2591-a3e7-6d83bd2a05ef.png)

やっぱハイライトがつくとテンションがあがりますね。
他にも括弧にカーソルを合わせると対応する括弧がハイライトされたり細かい部分の挙動が改善されている印象でした。
Windowsでbabun上でTab Completionが効かなかったのもなんか直ってた…

## Calendar

Elixir 1.3で追加された`Calendar`型に`day_of_week/3`などが追加され強化されました。

```elixir
iex(35)> Calendar.ISO.day_of_week(2016, 12, 12)
1
```

## Task.async_stream

並行タスク処理の強化でTaskモジュールに`async_stream`が追加されました。
今まではEnum出来るコレクションに対しては`Task.async`と`Task.await`を使って以下のように処理していたのを

```elixir
iex(1)> collection = [1, 2]
[1, 2]
iex(2)> collection \
...(2)> |> Enum.map(&Task.async(IO, :inspect, [&1])) \
...(2)> |> Enum.map(&Task.await/1)
1
2
[1, 2]
```

以下のように書くことが出来るようになりました

```elixir
iex(1)> collection = [1, 2]
[1, 2]
iex(2)> collection \
...(2)> |> Task.async_stream(IO, :inspect, [], max_concurrency: System.schedulers_online) \
...(2)> |> Enum.to_list
2
1
[ok: 1, ok: 2]
```

Enumを受け取ってEnumを返しているので今までの`async`してから`await`していたより記述がわかりやすいですね。あと`max_concurrency`で最大並行数とか指定できます。

## Application inference

mixのapplicationに明示的にdependencyを追加しなくても勝手にdependencyから検出してビルドしてくれるようになった（！）

これが

```elixir
def application do
  [applications: [:logger, :plug, :postgrex]]
end

def deps do
  [{:plug, "~> 1.2"},
   {:postgrex, "~> 1.0"}]
end
```

こう

```elixir
def application do
  [extra_applications: [:logger]]
end

def deps do
  [{:plug, "~> 1.2"},
   {:postgrex, "~> 1.0"}]
end
```

よく`application`に追加したライブラリを書き忘れてno process errorになっていたりしたので地味にありがたいですね。
`logger`などランタイムで必要な一部のライブラリは`extra_applications`として追加する必要はあるみたいです。
`distillery`などランタイムでは必要ないライブラリ(デプロイ、ビルド時のみに必要なもの)などは`runtime: false`オプションで明示的にランタイムから外せるみたいです。

```elixir
{:distillery, "> 0.0.0", runtime: false}
```

## Mix install from SCM

`escript`(Elixirで書けるCLIツール)をmix経由でGitHubやhexから直接インストール出来るようになりました

例えば`ex_doc`を入れたい場合は以下のように`mix escript.install hex ex_doc`を叩くとHOMEディレクトリ以下の`.mix/escripts`にバイナリがインストールされます

```bash
$ mix escript.install hex ex_doc

Running dependency resolution
Dependency resolution completed
  earmark: 1.0.3
  ex_doc: 0.14.5
* Getting ex_doc (Hex package)
  Checking package (https://repo.hex.pm/tarballs/ex_doc-0.14.5.tar)
  Fetched package
* Getting earmark (Hex package)
  Checking package (https://repo.hex.pm/tarballs/earmark-1.0.3.tar)
  Fetched package
==> earmark
Compiling 3 files (.erl)
Compiling 19 files (.ex)
warning: variable "option_related_help" does not exist and is being expanded to "option_related_help
()", please use parentheses to remove the ambiguity or change the variable name
  lib/earmark/cli.ex:46

warning: variable "all_converters" does not exist and is being expanded to "all_converters()", pleas
e use parentheses to remove the ambiguity or change the variable name
  lib/earmark/inline.ex:19

warning: variable "all_converters" does not exist and is being expanded to "all_converters()", pleas
e use parentheses to remove the ambiguity or change the variable name
  lib/earmark/inline.ex:54

warning: variable "all_converters" does not exist and is being expanded to "all_converters()", pleas
e use parentheses to remove the ambiguity or change the variable name
  lib/earmark/inline.ex:245

warning: module attribute @id_close_rgx was set but never used
  lib/earmark/scanner.ex:9

Generated earmark app
==> ex_doc
Compiling 16 files (.ex)
Generated ex_doc app
Generated escript ex_doc with MIX_ENV=prod
Are you sure you want to install escript "ex_doc"? [Yn] y
* creating /root/.mix/escripts/ex_doc

warning: you must append "/root/.mix/escripts" to your PATH if you want to invoke escripts by name
```

簡単ですね。

## まとめ

Elixir 1.4で導入された機能を一通り眺めてみた。
全体的に並行プラグラミングが強化されつつ細かい点も改善されたリリースになったんじゃないでしょうか。
細かい変更点は[CHANGELOG.md](https://github.com/elixir-lang/elixir/blob/v1.4/CHANGELOG.md)にあるのでそちらをご確認ください。

今すぐ試したい方はDockerHubに`1.4.0-rc.1`のイメージを上げておいたのでそちらで試してみてください

```elixir
docker run -it --entrypoint sh shufo/phoenix:1.4.0-rc.1
```