環境
sh
$ lsb_release -d
Description: Ubuntu 18.04.2 LTS
$ elixir -v
Erlang/OTP 21 [erts-10.3.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Elixir 1.8.1 (compiled with Erlang/OTP 20)
はじめに
Elixir episode:0 実行ファイル のプロジェクトを使う。
いや、使わんでもいい。
Fg.List
touch lib/fg/list.ex
fg/lib/fg/list.ex
defmodule Fg.List do
def cons(x, y), do: [x | y]
def head([x | _y]), do: x
def tail([_x | y]), do: y
end
$ iex -S mix
iex
iex(1)> alias Fg.List, as: L
Fg.List
iex(2)> list = L.cons(1, L.cons(2, L.cons(3, [])))
[1, 2, 3]
iex(3)> L.head(list)
1
iex(4)> L.tail(list)
[2, 3]
iex(5)> L.head([1])
1
iex(6)> L.tail([1])
[]
おっけー?
無名関数
$ iex
iex
iex(1)> first = fn (x, _y) -> x end
# Function<12.128620087/2 in :erl_eval.expr/5>
iex(2)> second = fn (_x, y) -> y end
# Function<12.128620087/2 in :erl_eval.expr/5>
iex(3)> first.(1, 2)
1
iex(4)> second.(1, 2)
2
カリー化
全ての関数を、1引数の関数で表現すること。
iex
iex(5)> first = fn x -> fn _y -> x end end
# Function<6.128620087/1 in :erl_eval.expr/5>
iex(6)> second = fn _x -> fn y -> y end end
# Function<6.128620087/1 in :erl_eval.expr/5>
iex(7)> first.(1).(2)
1
iex(8)> second.(1).(2)
2
なんの役に立つのか?実利的にはあまり役に立たない。
関数とは何か?と考える場合には役に立つ。
ざっくり言えば、関数の一般化である。
ラムダ算法
ラムダ算法の説明はしない。
以下はカリー化を施した関数によって、
関数のみでリスト(データ構造)を作れることを示している。
iex
iex(9)> cons = fn x -> fn y ->
...(9)> fn selector -> selector.(x).(y) end
...(9)> end end
# Function<6.128620087/1 in :erl_eval.expr/5>
iex(10)> head = fn list -> list.(first) end
# Function<6.128620087/1 in :erl_eval.expr/5>
iex(11)> tail = fn list -> list.(second) end
# Function<6.128620087/1 in :erl_eval.expr/5>
iex(12)> list = cons.(1).(2)
# Function<6.128620087/1 in :erl_eval.expr/5>
iex(13)> head.(list)
1
iex(14)> tail.(list)
2
iex(15)> list = cons.(1).(cons.(2).(cons.(3).(nil)))
# Function<6.128620087/1 in :erl_eval.expr/5>
iex(16)> head.(list)
1
iex(17)> head.(tail.(list))
2
iex(18)> head.(tail.(tail.(list)))
3
iex(19)> tail.(tail.(tail.(list)))
nil
Fg.List を書き換える
fg/lib/fg/list.ex
defmodule Fg.List do
def cons(x, y) do
fn selector when is_function(selector, 2) ->
selector.(x, y)
end
end
def head(list) when is_function(list, 1) do
fn x, _y -> x end # first selector
|> list.()
end
def tail(list) when is_function(list, 1) do
fn _x, y -> y end # second selector
|> list.()
end
end
$ iex -S mix
iex
iex(1)> alias Fg.List, as: L
Fg.List
iex(2)> list = L.cons(1, L.cons(2, L.cons(3, nil)))
# Function<0.76478687/1 in Fg.List.cons/2>
iex(3)> L.head(list)
1
iex(4)> L.tail(list)
# Function<0.76478687/1 in Fg.List.cons/2>
iex(5)> L.tail(list) |> L.head()
2
iex(6)> L.tail(list) |> L.tail() |> L.head()
3
iex(7)> L.tail(list) |> L.tail() |> L.tail()
nil
また来てくれよなーノシ