プログラミング Elixir 第五章

# 概要

[プログラミング Elixir](https://www.amazon.co.jp/dp/4274219151)

無名関数について解説している章。
無名関数とは、名前をつけずに関数自体を変数に束縛して、それを呼び出す方法。

```iex:例
iex> sum = fn (a, b) -> a + b end
#Function<12.52032458/2 in :erl_eval.expr/5>
iex> sum.(1, 2)
3
```

例の様に `引数 -> 関数の実装(ボディ)` を `fn ... end` で囲み定義する。
呼び出す際は、`.()` で呼び出す。名前付き関数とは違い、`.`, `()` が必要となる。

関数定義の際には `()` は省略可能である。

```iex
iex> sum = fn a, b -> a + b end
#Function<12.52032458/2 in :erl_eval.expr/5>
iex> sum.(1, 2)
3
iex> say = fn -> IO.puts "hello" end
#Function<20.52032458/0 in :erl_eval.expr/5>
iex> say.()
hello
```

# 関数とパターンマッチ

`sum.(1, 2)` と呼ぶと `a` に 1、`b` に 2 が代入されている様に感じるが、Elixir に代入は存在しない（[プログラミング Elixir 第二章](http://qiita.com/pekepek/items/cea917c8ed445da1bc24)）。1 は a に代入ではなく、束縛され、パターンマッチが行われている。

以下の例を見るとわかりやすい。

```iex
iex> match? = fn (1, 2) -> "match" end
#Function<12.52032458/2 in :erl_eval.expr/5>
iex> match?.(1, 2)
"match"
iex> match?.(1, 3)
** (FunctionClauseError) no function clause matching in :erl_eval."-inside-an-interpreted-fun-"/2
```

# 関数に複数のボディを持たせる

Elixir は一つの関数に、異なる型・内容の引数、それに対応したボディを実装することができる。これとパターンマッチの機能を使って、便利な関数を実装する事ができる。

```iex
iex> fn
...>   (pattern 1) -> hogehoge
...>   (pattern 2) -> fugafuga
...>   (pattern 3) -> piyopiyo
...> end
```

上記の様なイメージで、パターンの異なる引数とボディを実装する。この際、引数の数を変えることはできない。
実行した際に `pattern 2` にマッチする値を渡した場合は `fugafuga` が実行される。

以下は、この機能をうまく使った無名関数の例である。

```iex
iex> file_open = fn
...>   {:ok, file} -> "Read data: #{IO.read(file, :line)}"
...>   {_, error} -> "Error: #{:file.format_error(error)}"
...> end
#Function<6.52032458/1 in :erl_eval.expr/5>

iex> file_open.(File.open("./sample.txt"))
"Read data: Hello!!"
iex> file_open.(File.open("./not_exist.txt"))
"Error: no such file or directory"
```

無名関数を見てみると 2 つの引数が定義されている。一つ目のタプルは `:ok` を第一要素に持ったタプル。二つ目のタプルは `_` を第一要素に持ち、どんな値でもマッチする。
これによって `File.open` の結果によって動作の異なる関数を実装することができる。

ここで、`:file` は Erlang の File モジュールを参照していて、それによって `format_error` メソッドを呼び出している。また、文字列で `#{}` で囲むと式が評価される。

# 関数を返す関数

関数を返す関数を定義することもできる

```iex
iex> hello = fn -> fn -> "Hello" end end
#Function<20.52032458/0 in :erl_eval.expr/5>
iex> hello.()
#Function<20.52032458/0 in :erl_eval.expr/5>
iex> hello.().()
"Hello"
```

`hello.()` で外側の関数が呼ばれ、内側の関数を返す。
そして、返ってきた内側の関数が `.()` で実行され、`Hello` を出力している。

書き方は改行とインデントを入れたり、内側の関数定義を括弧でくくることもできる。

```iex
iex> hello.().()
"Hello"
iex> hello = fn ->
...>           fn ->
...>             "Hello"
...>           end
...>         end
#Function<20.52032458/0 in :erl_eval.expr/5>
iex> hello = fn -> (fn -> "Hello" end) end
#Function<20.52032458/0 in :erl_eval.expr/5>
```

## 束縛を引き継ぐ

以下は外側で束縛した値を内側の関数が引き継いでいる例

```iex
iex> greeter = fn name -> (fn -> "Hello #{name}" end) end
#Function<6.52032458/1 in :erl_eval.expr/5>
iex> dave_greeter = greeter.("dave")
#Function<20.52032458/0 in :erl_eval.expr/5>
iex> dave_greeter.()
"Hello dave"
```

このように外側で束縛した値は、内側の関数の変数にも束縛されていて、後から呼び出すことができる。

これを利用することで以下のような関数を作ることができる。

```iex
iex> add = fn n -> (fn m -> n + m end) end
#Function<6.52032458/1 in :erl_eval.expr/5>
iex> add_one = add.(1)
#Function<6.52032458/1 in :erl_eval.expr/5>
iex> add_three = add.(3)
#Function<6.52032458/1 in :erl_eval.expr/5>
iex> add_one.(2)
3
iex> add_three.(2)
5
```

`add_one` は `n` に 1 が束縛されているので、引数に 1 を足す関数。
そして、`add_three` `n` に 3 が束縛されているので、引数に 3 を足す関数となる。

# 関数を引数として使う

これまで見てきたように関数はただの型の一つなので、引数として使用できる。

```iex
iex> divide_half = fn n -> n / 2 end
#Function<6.52032458/1 in :erl_eval.expr/5>
iex> executor = fn (func, value) -> func.(value) end
#Function<12.52032458/2 in :erl_eval.expr/5>
iex> executor.(divide_half, 6)
3.0
```

2 で割る関数を executor に渡して、executor は第二引数の value を func に渡して実行している。

# 引数にピン演算子を使う

[ピン演算子](http://qiita.com/pekepek/items/cea917c8ed445da1bc24#_-%E3%81%A8-)を変数につけるとパターンで使うことができるが、ピン演算子は引数にも使用できる。

```iex
iex> greet = fn name, greeting ->
...>           fn
...>             (^name) -> "#{greeting} #{name}"
...>             (_)     -> "I don't know you"
...>           end
...>         end
#Function<12.52032458/2 in :erl_eval.expr/5>

iex> hello = greet.("Tom", "Hello")
#Function<6.52032458/1 in :erl_eval.expr/5>
iex> hello.("Tom")
"Hello Tom"
iex> hello.("Mike")
"I don't know you"
```

`greet.("Tom", "Hello")` で `name` に `"Tom"` を束縛して、その後はパターンマッチを行っている。マッチした時のみ一行目が実行される。

# & 記法

`fn () -> ... end` は & 記法を使って短く書くこともできる

```iex
iex> add_one = &(&1 + 1) # fn (n) -> n + 1 end
#Function<6.52032458/1 in :erl_eval.expr/5>
iex> add_one.(2)
3
```

引数を増やしたい場合は  &1, &2, &3 ... としていく

また、Elixir は既にある関数を呼び出すだけの無名関数を作成した場合、無名関数を作らずに直接参照する様に最適化する。

```iex
iex> add = &(&1 + &2)
&:erlang.+/2
iex> add.(1, 2)
3
```

ただし、引数をそのままの順番で渡す必要がある。

```iex
iex> add = &(&2 + &1)
#Function<12.52032458/2 in :erl_eval.expr/5>
```

また、リストやタプルのリテラルを関数に変換する事もできる。
以下は和と差を返す関数。

```iex
iex> add_and_diff = &{ (&1 + &2), (&1 - &2) }
#Function<12.52032458/2 in :erl_eval.expr/5>
iex> add_and_diff.(3, 1)
{4, 2}

iex> add_and_diff = &[ (&1 + &2), (&1 - &2) ]
#Function<12.52032458/2 in :erl_eval.expr/5>
iex> add_and_diff.(3, 1)
[4, 2]
```

また、& 記法は既に存在する関数の名前とアリティ（パラメーター数）を渡すと、それを呼び出す無名関数を返す。

```iex
iex> add = &+/2
&:erlang.+/2
iex> add.(1, 2)
3

iex> l = &length/1
&:erlang.length/1
iex> l.([1, 2, 3, 4])
4
```