DEV Community

gumi TECH for gumi TECH Blog

Posted on

Elixir 1.8.0が正規リリースされた

Elixir 1.8が正規リリースされました。おもに、基盤となる部分の改善が加えられたということです。本稿は、リリースノート(英語)をもとに、つぎの4つの項目についてご説明します。

  • カスタム構造検査
  • タイムゾーンデータベースへの対応
  • コンパイルの高速化とその他のパフォーマンス改善
  • 計測・監視機能の改善と$callersの追加

カスタム構造検査

ElixirのInspectプロトコルの実装が、データを抽出できるようになりました。データ構造を検査するときに、フィールドが簡単にフィルタリングできるのです。ユーザーのデータ構造がプライバシーに関わる情報を含んでいる場合などに使えます。たとえばつぎの構造体で、:email:encrypted_passwordといった個人情報です。inspect/2関数で調べると、デフォルトではすべてのフィールドが取り出されます。

defmodule User do
  defstruct [:id, :name, :age, :email, :encrypted_password]
end

ログやエラーレポートなどに出力したくないデータについては、Inspectプロトコルのカスタム実装が定められます。Elixir 1.8ではさらに、Inspectプロトコルに@deriveを加えて、簡単にデータが抽出できるようになりました。オプション:onlyでフィールドを選べば、他は表示されません。

defmodule User do
  @derive {Inspect, only: [:id, :name, :age]}
  defstruct [:id, :name, :age, :email, :encrypted_password]
end

inspect(%User{id: 1, name: "Homer", age: 33, address: "742 Evergreen Terrace"})
#=> #User<id: 1, name: "Homer", age: 33, ...>

オプションにはもうひとつ、表示しないフィールドを絞る:exceptがあります(@derive {Inspect, except: [...]})。

タイムゾーンデータベースへの対応

Elixir 1.3では日付と時刻を扱うために、つぎの4つのカレンダー型を備えました。

  • Time
  • Date
  • NaiveDateTime(タイムゾーンなし)
  • DateTime(タイムゾーンあり)

その後のアップデートでカレンダー型には改善が加えられたものの、DateTimeモジュールがタイムゾーンデータベースに対応するにはいたりませんでした。

Elixir 1.8には新たにCalendar.TimeZoneDatabaseビヘイビアが加わっています。このビヘイビアで、開発者は独自にタイムゾーンデータベースを取り込めるのです。タイムゾーンのビヘイビアに規約を明示的に定めることで、ElixirのDateTime APIが拡張できます。たとえば、DateTimeのタイムゾーンを変えるのがDateTime.shift_zone/3関数です。Elixirはデフォルトでは、UTCのみを扱うCalendar.UTCOnlyTimeZoneDatabaseをタイムゾーンデータベースとして備えます。

そのほかカレンダーに関わる改善として、つぎの4つの関数が新しく加わりました。

コンパイルの高速化とその他のパフォーマンス改善

昨年コンパイラを改善したことにより、Elixir 1.8のコンパイルは平均5%速くなりました。さらに今回、コンパイル時間を縮め、快適な開発ができるための改善を加えています。

つぎの場合に、コンパイラはより効率的なコードを生成します。

  • ガードにおける範囲確認(x in y..zなど)
  • 文字リストの内挿('foo #{bar} baz'など)
  • Recordモジュールのレコード操作

EExテンプレートには共通の最適化を加え、コードは小さくなり、実行が速まりました。

計測・監視機能の改善と$callersの追加

Taskモジュールは、軽いプロセスをいくつも生成して同時に処理するためによく用いられます。そして、Elixirが新たなプロセスをつくったとき、その親を示すために付されるのが$ancestorsキーです。この情報は、計測ツールが複数のプロセス内で起こるイベントの関係を追跡するために使われます。ただ多くの場合、$ancestorsの情報だけでは足りません。

タスクはスーパーバイザーのもとで開始することが推奨されています。見通しがよくなり、ノードが落ちたときにタスクをどう終わらせるか管理できるからです。そのためには、たとえばつぎのコードのようにタスクを実行するでしょう。タスクはこのコードによりつくられます。けれどこのとき、タスクを直接生成した親はスーパーバイザーになってしまうのです。スーパーバイザーはタスクの$ancestorsリストに含まれます。けれど、コードとの関係は失われてしまうのです。

Task.Supervisor.start_child(MySupervisor, task_specification)

Elixir 1.8では、コードとタスクとの関係は、プロセスの辞書から$callersキーで追跡できるようになりました。このキーは、$ancestorsとともに使えます。前掲のTask.Supervisorを用いたコードでは、つぎのような関係になるでしょう。

elixir_v18_001_02.png

このとき、ふたつのキー$ancestors$callersに加えられるのはつぎのとおりです。

elixir_v18_001_01.png

スーパーバイザーは使わず、コードから直にタスクをつくると、コードが走っているプロセスは、$ancestors$callersの両方のリストに入ります。

この機能により、計測と監視のツールが、システム内で起きたイベントの追跡や関連づけをしやすくなるでしょう。さらに、Ectoサンドボックスのようなツールでも使えるのです。Ectoサンドボックスにより、データベースに対するテストは複数同時にできます。そこで用いられるトランザクションと所有メカニズムには、各プロセスが明示的に割り当てられています。$callersがないと、データベースに問い合わせるタスクをつくっても、タスクはどこから呼び出されたのかわからず、どこに接続されているのか知ることができません。すると多くの場合、タスクに依存する機能は同時にテストできないことになります。$callersによりこれが簡単にわかり、マシンのパワーをフルに活かしたテストが可能になるのです。

詳しい変更についてはリリースノート(英語)をご参照ください。

Discussion (0)