[翻訳] ElixirとErlangの変数を比較する

[José Valim](http://blog.plataformatec.com.br/?author=4)さん[^1]の2016年1月12日付のブログ記事"[Comparing Elixir and Erlang variables](http://blog.plataformatec.com.br/2016/01/comparing-elixir-and-erlang-variables/)"の翻訳です。
[^1]: 言わずと知れたElixir作者。
昔[**Erlang**の変数について勉強したとき](http://www.ymotongpoo.com/works/lyse-ja/ja/03_starting_out_for_real.html#invariable-variables)、再代入不可の変数って定数？、どうやって使うの？[^2]と思ったものです。確かに一度代入したものが変更できないのであればプログラムの見通しはよくなります。一方で[**Elixir**の変数](http://elixir-lang.org/crash-course.html#variable-names)は一見すると再代入可能に見えます。再代入禁止の方がバグが混入しにくい？本当？
この辺りについてジョゼ自ら説明します。

[^2]: 代入された結果を使い回す以外に、引数や再帰呼び出しした先の変数を使って状態を受け渡すことで見た目状態変化させるように使いますね。

-----

時々Erlangのプログラマは「Elixirの変数は隠れたバグの温床になるのでは」と心配するようです。この記事ではそういう心配についてと、Erlangでの変数も「隠れたバグ」を生み出しかねず、一方でElixirはそれらのうちのいくつかを除去するということについて議論します。

話を始める前に短い放棄声明を…: Elixirは可変(mutable)な変数を持っていません。あるのは再バインドです。可変性はよくストレージと関連付けられます。Elixirでは保存された値を変更することはできません(Erlangと同じです)。可変な変数の例としてF#を見てみましょう。F#では明示的に`mutable`キーワード(例えば`let mutable x = 5`)によりインナーループの内側の変数を変更することができるようになり(`Enum.map`の内側やリスト内包表記と等価です)、ループを抜けた後も変更された状態を見ることができます。これが可変性で、ElixirやErlangではプロセスや[ETS](http://www.ymotongpoo.com/works/lyse-ja/ja/28_ets.html)[^3]と言ったストレージを明確に使わないかぎり不可能なことなのです。
[^3]: Erlang Term Storage、Erlang用のインメモリーデータベースです。


話を基に戻して。この記事ではコードを変更した時の隠れたバグの可能性について調査します。これらのバグはErlang及びElixirの変数が**暗黙の挙動**を持っているために起きます。Elixirの再バインドは暗黙的に行われますし、Erlangのパターンマッチも暗黙的に行われます。これらのバグはプログラマが変数を追加したり削除したりする際にそのコンテキストを十分に意識していないと発生することがあります。

ではいくつか例を見てみましょう。次のようなElixirコードを考えてみてください:

```ex
foo_bar = ...
# 何らかのコード
use_foo_bar(foo_bar)
```

さてここで`foo_bar`が上記のスニペットより前に現れていたら何が起きるでしょうか。

```ex
foo_bar = ... # 新しく追加された行
foo_bar = ...
# 何らかのコード
use_foo_bar(foo_bar)
```
このコードはコンパイラは新しく追加された`foo_bar`が使われないままだという警告を出すでしょうが[^4]正しく動作するでしょう。しかし、もし新しく追加された行が`foo_bar`より後に現れていたら何が起きるでしょうか。

[^4]: `warning: variable foo_bar is unused`と表示されます。

```ex
foo_bar = ...
# 何らかのコード
foo_bar = ... # 新しく追加された行
use_foo_bar(foo_bar)
```
もし`use_foo_bar`関数を最初の変数`foo_bar`の値で使うつもりだったとしたらこれでは意味合いが変わってきてしまいます。まったく、不注意な変更はバグを呼ぶものです。

ではErlangを調べてみましょう。コードは:

```erlang
FooBar = ...
% 何らかのコード
use_foo_bar(FooBar)
```
定義より前に`FooBar`が現れたら？

```erlang
FooBar = ... % 新しく追加された行
FooBar = ... % ここの、元からあった行はエラーになる
% 何らかのコード
use_foo_bar(FooBar)
```
Erlangのこのコードは何も言わずに継続されることなく実行時にクラッシュします[^5]。確かによりよいかもしれませんがErlangで変数を使う際にはその変数が以降マッチされないことを保証しなければなりません。この場合`FooBar`は二度と割り当てもされずマッチもされないことを、です。

[^5]: 例えば`** exception error: no match of right hand side value...`というエラー。

では定義の後にこの行を置いたら？

```erlang
FooBar = ...
% 何らかのコード
FooBar = ... % 新しく追加された行はエラーになる
use_foo_bar(FooBar)
```
今度は新しく追加された行がクラッシュします。言い換えるとErlangの暗黙のマッチングにより変数を使った**後**はコード内を全く心配しなくてよいということです。しかし、使う**前**については十分に注意しなければなりません。先に書かれたコードが先々の変数と暗黙にマッチできてしまうからです。

別の言い方をすればElixirはある変数を使った後のコードに注意が必要で、Erlangはある変数を使う前のコードに注意が必要です。Erlangでは変数を使うより前の全てのコードについてあなたが知っていなければなりません。Erlangの良い点のひとつはそのマッチで明示的にコードがクラッシュするということです。

しかし、case式を考えるとことはさらに複雑になっていきます。

## Case式

新しい変数をcase式の中でマッチさせたい。Elixirではこんな感じに書けます:

```ex
case some_expr() do
  {:ok, safe_value} -> perform_something_safe()
  _ -> perform_something_unsafe()
end
```
では、うっかりElixirで変数`safe_value`をこのcase式より前に使ってしまったら。

```ex
safe_value = ... # 新しく追加された行
# 何らかのコード
case some_expr() do
  {:ok, safe_value} -> perform_something_safe()
  _ -> perform_something_unsafe()
end
```
何も起きません。コードは再バインドのためにうまく動作します。

ではErlangでは何が起きるでしょう:

```erlang
case some_expr() of
  {ok, SafeValue} -> perform_something_safe();
  _ -> perform_something_unsafe()
end
```
さて、前に変数を置いたら何が起きるでしょう？

```erlang
SafeValue = ... % 新しく追加された行
% 何らかのコード
case some_expr() of
  {ok, SafeValue} -> perform_something_safe();
  _ -> perform_something_unsafe()
end
```

さて、あなたは今知らないうちに危険な潜在バグをコードに入れたのです！繰り返しますが、Erlangは**暗黙的に**マッチするので、case文の最初の節では`SafeValue`は絶対にバインドされないのにマッチだけは成功してしまうためperforme_something_unsafe()の方が実行されてしまうかもしれません[^6]。

[^6]: この説明だと少しわかりにくいのですがsome_expr()が正常終了した場合に{ok, "何らかの値"}を返すとすると、その場合SafeValueがそれまでに使われていなければSafeValueには"何らかの値"が入りperform_something_safe()が実行されることになります。もし先にSafeValueに先に値をバインドしてしまっていたとすると、このcase文はsome_expr()がどんな値を返しても、バインドしたのがたまたま"何らかの値"の場合以外では必ず失敗して_節の方が実行されます。意図した動作と全く異なる動作なので原因に気づきにくいでしょうね。

Erlangにおいて同様のバグは既存の変数とマッチングさせているのにその変数を削除してしまった場合にも起きます[^7]。では以下の様なElixirの動作するコードがあったとしてみてください:

[^7]: 今と逆のケースですね。

```ex
safe_value = ...
# 何らかのコード
case some_expr() do
  {:ok, ^safe_value} -> perform_something_safe()
  _ -> perform_something_unsafe()
end
```
Elixirのパターンマッチは明示的なので`safe_value`の定義を削除したとしたらこのコードはコンパイルが通りません[^8]。ではErlangでちゃんと動くバージョンを見てみましょう。

[^8]: case文の最初の節で^(ピン演算子)で再バインド禁止してますからね。** (CompileError) nantoka.ex:行番号: unbound variable ^safe_valueみたいなエラーが出てしまいます。

```erlang
SafeValue = ...
% 何らかのコード
case some_expr() of
  {ok, SafeValue} -> perform_something_safe();
  _ -> perform_something_unsafe()
end
```
ここで変数`SafeValue`を削除したとするとcase文の最初の節は`SafeValue`とマッチするのではなくバインドされるので、またも知らないうちにこのコードの挙動を変えてしまいました！どちらの場合についてもElixirのやり方なら防げるのにまたもバグです。

この点についてElixirでは:

* 変数を新しく使う場合はそれ以降のコードを全て分析する必要がある。ちゃんと分析できないとバグが出るかもしれない。
* 変数のマッチングは常に安全である。なぜなら変数の再バインドがあるし、明示的にマッチさせるためには^(ピン演算子)があるから。

一方Erlangは:

* 変数を新しく使う場合はそれより前、及びそれより後のコードを全て分析してそれがマッチングか割り当てかを明確にしておく必要がある。ちゃんと分析できないと実行時にクラッシュするだろう。
* 変数を新しく使う場合はそれ以降のコードを全て分析して後のcase文の意味を変えてしまわないか確認しておく必要がある。ちゃんと分析できないとバグが出るかもしれない。
* 変数を削除する場合はそれ以降のコードを全て分析してあとにあるcase文の意味を変えてしまわないか確認しておく必要がある。ちゃんと分析できないとバグが出るかもしれない。


##通し番号付きの変数

最初にElixirで新しく変数`foo_bar`を使うとその変数が後で使われている場合はコードの意味が変わってしまうと述べました。しかしそういうケースの大半は意図して行うものです。例えばElixirでは[^9]:

[^9]: こういう場合は普通はみんな大好きパイプライン演算子(|>)を使うと思いますが…

```ex
foo_bar = step1()
foo_bar = step2(foo_bar)
foo_bar = step3(foo_bar)
# 何らかのコード
use_foo_bar(foo_bar)
```

Erlangで:は[^10]

[^10]: Erlangは変数の再バインドというか異なる値とのパターンマッチを禁止していますからこのように通し番号でもつけて別の変数を使わないといけません。

```erlang
FooBar0 = step1(),
FooBar1 = step2(FooBar0),
FooBar2 = step3(FooBar1),
% 何らかのコード
use_foo_bar(FooBar2)
```

では新しく`foo_bar`(`step_4`)をElixirのコードに入れるとすると？

```ex
foo_bar = step1()
foo_bar = step2(foo_bar)
foo_bar = step3(foo_bar)
foo_bar = step4(foo_bar) # 新しく追加された行
# 何らかのコード
use_foo_bar(foo_bar)
```
これで動きます。ではErlangは？

```erlang
FooBar0 = step1(),
FooBar1 = step2(FooBar0),
FooBar2 = step3(FooBar1),
FooBar3 = step4(FooBar2),
% 何らかのコード
use_foo_bar(FooBar2) % 全てのFooBar2を書き換えないといけない
```
もし開発者が新しい変数を入れて以降の`FooBar2`を書き換えるのを忘れるとプログラムの意味が変わってしまい、Elixirなら再バインドしたはずのバグを生じてしまいます。これは特にすべて変更した後にひとつだけ変数を変更し忘れていたような場合に厄介です。なぜならそのようなコードは「変数が使われていません」といった警告を出さないからです。また中間状態を追加(例えば`step2`と`step2`の間に)するとさらにエラーを生じがちになります。

通し番号付きの変数の方が後ろに書かれたコードで`FooBar2`も
`FooBar3`も使えるという利点があるではないかという人もいるでしょう。例えば:

```erlang
FooBar0 = step1(),
FooBar1 = step2(FooBar0),
FooBar2 = step3(FooBar1),
FooBar3 = step4(FooBar2),
% 何らかのコード
use_foo_bar(FooBar2),
something_else(FooBar3)
```
しかし私は上記のコードはよくない実例だと考えます。なぜならば`FooBar2`という名前には`FooBar3`とは違うという事情を示すためのヒントが何も含まれていないからです。この場合だとコードの一部がどうして他の名前を使わずにこの名前を使っているのかについて変数名は全く反映できていません。明確な意味を持つ名前を付けるほうが通し番号付きの変数よりチームメンバーにとっては遥かにマシでしょう。

## まとめ

ElixirとErlangの変数はそれぞれ再バインドとパターンマッチという暗黙の挙動を提供しているので既存のコードについて変数を追加したり削除したりする場合には注意が必要です。そのため、Elixirが隠れたバグの温床になるような場合はErlangでも同じようなバグを違った状況下で発生させることを見てきました。それだけではなくErlangは新しい変数を追加した場合そこより前だけではなく、後ろについても知識を持っていないといけません。Elixirなら後ろについてだけ知識が必要です。双方の言語でこれらのバグを回避するには**変数の再バインドまたはパターンマッチ操作を明示的に行う**かあるいはどちらも禁止するしかありませんが、どちらの言語もそれはできません。

この記事を読んで「僕のコードには関係ないや」という反応をする人もいるかもしれませんが実のところどんな小さな関数でもこれは起きているのです：

* http://erlang.org/pipermail/erlang-questions/2010-January/048742.html
* http://erlang.org/pipermail/erlang-questions/2010-January/048762.html
* http://erlang.org/pipermail/erlang-questions/2010-January/048767.html

一方でErlangやElixirでプログラムを書くともっとバグが出やすいということではありません。結局のところ、Erlangの開発者たちは何十年にもわたって堅牢なコードを書いてきました。こういう「変な癖」はどんな言語にも存在し、プログラマたちが経験を積むにつれ最終的にはそれらは自家薬籠中の物となります。それはまさに「僕のコードには関係ないや」という言葉が出る所以です。

最後に、どんな言語でも文脈に注意を向けないままだと安全なコード変更を保証してはくれないでしょう。常に「隠れたバグ」が起こりえます。例えばClojureやJavaSctiptやRubyと言った言語であっても、変数及び関数の名前は同じ名前空間の中にあり新しく変数を使う場合は関数呼出しの意味を変えてしまうかもしれません[^11]。ErlangもElixirも変数用にひとつ、関数用にひとつ、計2つの名前空間を持っているのである種の「隠れたバグ」からは守られています。

[^11]: ClojureはLisp-1なので変数と関数は同じ名前空間にあります。

さらに型システムやコンパイラの警告、テストスイートは全てこれらの問題を解決するためのテクニックです。言語によってはElixirのパイプライン演算子(`|>`)のように繰り返しのコードをさらに読みやすくエラーが起きにくい形に書き換えるのを手助けするパターンを提供しています。

少なくともこの記事によってElixirの変数はErlangのより安全ではない(またはその逆)と言ったクレームにケリが付いてくれることを期待しています。

謝辞：Joe Armstrong, Saša Juric, James Fish, Chris McCord, Bryan Hunter, Sean Cribbs及びAnthony Ramineにはこの記事をレビューしてもらいフィードバックをいただきました[^12]。

[^12]: なにげにすごいメンバー…

-----