この記事はElixir (その2)とPhoenix Advent Calendar 2016 12日目の記事です。
Elixir 1.3のリリースからはや6ヶ月、Elixir v.1.1より大体6ヶ月ごとにマイナーリリースをしているようで、順調にいけば年明けにはv.1.4の正式版がリリースされそうです。
ということで来るべきElixir 1.4で新しく追加される機能の一部を紹介します。
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されるとキーは自動的に削除されます。
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を実装出来ます。
とのことです。
ドキュメントにDispatcherやPubSubの説明の説明が書いてあったので参考に動かしてみます。
- Dispatcher
/ # 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
/ # 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のようなことが出来るModuleみたいです。gprocと同様にETSを使って実現し、ETSテーブルのshardingなどで性能を高めているそうです。
gprocと同様に複数のプロセスに同じ名前を付けたり一つのプロセスに複数の名前を登録出来るのでPubSubやDispatcherとしてEvent Handlingなど汎用的に使えます。
Elixirでgprocを使う例はあるけどインターフェースがErlangになるので標準でモジュールを提供してくれるのはありがたいですね。
Event HandlingするのにはGenEventというモジュールが既にあるけどGenEvent特有の問題もあるみたいで代わりとしてこれからRegistryを使っていけそうです。
Syntax coloring
ANSI Syntax Highrightがiexに来ました。
やっぱハイライトがつくとテンションがあがりますね。
他にも括弧にカーソルを合わせると対応する括弧がハイライトされたり細かい部分の挙動が改善されている印象でした。
Windowsでbabun上でTab Completionが効かなかったのもなんか直ってた…
Calendar
Elixir 1.3で追加されたCalendar
型にday_of_week/3
などが追加され強化されました。
iex(35)> Calendar.ISO.day_of_week(2016, 12, 12)
1
Task.async_stream
並行タスク処理の強化でTaskモジュールにasync_stream
が追加されました。
今まではEnum出来るコレクションに対してはTask.async
とTask.await
を使って以下のように処理していたのを
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]
以下のように書くことが出来るようになりました
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から検出してビルドしてくれるようになった(!)
これが
def application do
[applications: [:logger, :plug, :postgrex]]
end
def deps do
[{:plug, "~> 1.2"},
{:postgrex, "~> 1.0"}]
end
こう
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
オプションで明示的にランタイムから外せるみたいです。
{: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
にバイナリがインストールされます
$ 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にあるのでそちらをご確認ください。
今すぐ試したい方はDockerHubに1.4.0-rc.1
のイメージを上げておいたのでそちらで試してみてください
docker run -it --entrypoint sh shufo/phoenix:1.4.0-rc.1