[翻訳] クロージャ：Elixir vs Ruby vs JavaScript

Hubert Łępickiさんの2015年6月14日付のブログ記事[Closures: Elixir vs. Ruby vs. JavaScript](https://www.amberbit.com/blog/2015/6/14/closures-elixir-vs-ruby-vs-javascript/)の翻訳です。

Wikipediaより
> クロージャ（クロージャー、英: closure）、関数閉包はプログラミング言語における関数オブジェクトの一種。いくつかの言語ではラムダ式や無名関数で実現している。引数以外の変数を実行時の環境ではなく、自身が定義された環境（静的スコープ）において解決することを特徴とする。関数とそれを評価する環境のペアであるともいえる。

大雑把に言うと例えば「大域変数は使いたくないけど(名前がぶつかるとかで)、プログラム全体で状態を保持したい場合なんかに使うアレ」です[^1]。
[^1]: もちろんそれだけじゃありませんが…。

こちらの[猿でもわかるクロージャ超入門　まとめ](http://dqn.sakusakutto.jp/2009/01/javascript_5.html)がわかりやすいです。ただこれもそうですが、ググってみるとなぜかJavaScriptの例が多いんですよね。で「外側の関数」とか「内側の関数」とか書いてある[^2]
[^2]: JavaScriptで自由にコンテキストを定義するには関数を定義するしかないため。

他の言語だともっとずっとシンプルですよ…というお話。もちろん我らが**Elixirが一番スッキリ**…というオチになっていますが。ただここで使われているJavaScriptやRubyのコードは「言語の制限事項や上のサイトみたいなことを知ってればどうということはない内容」でもあります(実際回避したコードも書いてあるし)。まあ気分気分。

ああ、この人もRailsで仕事してる人じゃんか…

--------

### tl;dr (長文注意。先にまとめ書く。) ###

**Elixir**のクロージャはイヤな副作用を避けられる。
**JavaScript**のクロージャはこの3つの中では最悪(驚き～)。
**Ruby**のクロージャは言語の持つ関数型的な要素のせいで少しマシだけど完璧じゃない。

### ブサイク：JavaScript ###

もし少しでもJavaScriptのコードを書いたことがあれば、クロージャの驚くべき副作用と変更可能(mutable)な状態に行き当たったことだろう。もっともよくある*何てことしてくれる！*な瞬間は配列に対して繰り返し操作をして、配列要素の現在のインデックスに基づいた何らかの作業をしたいと思った時に訪れる。

```js
var functions = []

for (var i=0; i<3; i+=1) {
  functions.push(function() {
    console.log(i);
  });
}

functions[0]()
functions[1]()
functions[2]()
```

実行してみよう：

```bash
$ node closure.js
3
3
3
```

げげ、何が起きたんだ？**JavaScript**の関数は定義されたところで環境を保存する。したがって変数*i*は関数呼出しのたびにアクセスされる。ところがその値は0から1へ、1から2へ、そして2から3へと変化したのだ。そして最後の値、つまりループの停止条件が真になったときの値が不意に、予想しない形で全ての関数呼出しの結果として表示されたのだ。

この問題を回避するための標準的な*ハック手法*を使ってみよう：

```js
var functions = []

for (var i=0; i<3; i+=1) {
  (function(i) {
    functions.push(function() {
      console.log(i);
    });
  })(i);
}

functions[0]()
functions[1]()
functions[2]()
```

```bash
$ node closure_fix.js
0
1
2
```

まあ、一応の改善は見た。元の無名関数を他の無名関数でラップすることで新しいコンテキストを作ってからその無名関数を呼びださなければいけなかったのだ。面白いね。

これが**[どの](http://api.jquery.com/jquery.each/)[JavaScript](https://docs.angularjs.org/api/ng/function/angular.forEach)[フレームワークや](http://guides.emberjs.com/v1.10.0/enumerables/)[ライブラリ](https://lodash.com/docs#forEach)**も何らかのイテレータのラッパーを実装しているかの理由だ[^3]。それで問題を解決しているのだ。
[^3]: それは言いすぎ。ラッパーで一手間減るのは事実だけどJavaScript慣れしてるプログラマなら必要とあらばラッパー使わないでも書いちゃうでしょう。

### バックシャン(振り向かなければ美人)：Ruby ###

**Ruby**ではこの問題は起こらない。いいね？同じコードをテスト用に書いてみよう：

```rb
closures = 3.times.map do |i|
  -> { puts i }
end

closures[0].call
closures[1].call
closures[2].call
```

```bash
$ ruby closure.rb
0
1
2
```

よーし、**Ruby**に乗り換えちゃうぞ…？そう慌てるな。上のコードは言語の標準ライブラリにある組み込みの素晴らしいイテレータを使ったからこそ動いたのだ。ほとんどの**Ruby**プログラマは同様のテクニックを使ってコーディングしているわけだが、あえて生の言語構造で同じものを書きなおしてみる：

```rb
closures = []

i = 0

while i<3 do
  closures.push(-> { puts i })
  i+=1
end

closures[0].call
closures[1].call
closures[2].call
```

```bash
$ ruby closure_broken.rb
3
3
3
```

くそー。


### かわいいやつ : Elixir ###

同じコードを**Elixir**で書いてみる：

```ex
closures = (0..2)
  |> Enum.map &( fn () -> IO.puts(&1) end)

closures
  |> Enum.each &(&1.())
```
[^4]
[^4]: &(...)は無名関数の定義の省略形。&(&1.())は fn x -> x.()と同じでこの場合変数closures(クロージャのリスト)から各クロージャがEnum.eachで順番に渡されて実行されることになる。

```bash
$ elixir closure_pretty.ex
0
1
2
```

**Elixir**が初めてという人のために。私はここで*すごいパイプ*演算子を使っている。正式名称は*パイプ*演算子だがほんとにすごいのでその名前で呼びたいと思っているぐらいだ。演算子 |> はメソッド呼び出しのチェインを逆向きにさせてくれる。一つ前の式の結果を次のメソッドの第一引数として引き渡すのだ。つまり

```ex
closures = Enum.map (0..2), &( fn () -> IO.puts(&1) end)
```
は

```ex
closures = (0..2)
  |> Enum.map &( fn () -> IO.puts(&1) end)
```
になる。

これはこれで動くのだが、誰かが「それ、やっぱり組み込みイテレータ使ってんじゃね？」と言いそうだ。ということでできるだけわかりやすーいコードに書きなおしてみる：

```ex
closures = []

i = 0
closures = closures ++ [fn -> IO.puts i end]

i = 1
closures = closures ++ [fn -> IO.puts i end]

i = 2
closures = closures ++ [fn -> IO.puts i end]

Enum.at(closures, 0).()
Enum.at(closures, 1).()
Enum.at(closures, 2).()
```

```bash
$ elixir closure_explict.ex
0
1
2
```

動くぞ！でもなぜ？**Elixir**は不変(immutable)データ構造を使っている。変数*i*に新しい値を代入したとき、変数の値は変更されていない(わかってるわかってる、これはパターンマッチングで通常の代入じゃないね)。この挙動は*i=1, i=2, i=3*が現在のスコープで効率よく新しい変数*i*を作っていると考えれば説明がつく。関数は同じ*i*というラベルをもった変数を参照しているが値は変更されないままだ。変数が後で再利用されるまでは。

考えてみればこのやり方でクロージャを定義するのはものすごく筋が通っている。このデフォルトの挙動はマルチスレッドのアプリケーションは言うに及ばず、シングルスレッドのコードにおいてもトラブルを避ける手助けになる。

### まとめ ###

**JavaScript**か**Ruby**でコーディングする場合、もしイテレータがあるのならそれを使おう。もし関数型プログラミングパターンを日々のコーディングスタイルに喜んで取り入れたいと言うのなら頭の痛い思いは多少減らせるはず…そう、**Elixir**を習えばね。