はじめに
「プログラミングElixir」の第20章、20.7に従い演算子の再定義に取り組みました。
これはまちがいなく危険な領域だ
本のp254には予めシステムが用意している演算子を再定義する方法について述べられています。これを使い、加算の+演算子をペアノの算法に置き換えてみます。
ペアノの算法
ペアノの公理系により自然数とその演算が厳密に定義されています。詳細はwikipediaをご覧ください。
https://ja.wikipedia.org/wiki/ペアノの公理
Elixirのリストを自然数として扱います。空リスト [] を0とします。[[]]は1、[[[]]]は2とします。後続数を表すsucc/1と ひとつ前の数を表すprev/1を定義します。そうすると加算は次のadd/2ように定義することができます。
defmodule Peano do
defmacro a + b do
quote do
add(unquote(a),unquote(b))
end
end
def succ(n) do
[n]
end
def prev([n]) do
n
end
def add(a,[]) do a end
def add(a,b) do
add(succ(a),prev(b))
end
end
試してみます。
iex(7)> Peano.add([[[]]],[[]])
[[[[]]]]
iex(8)>
演算子の再定義
ElixirにはLisp並みに柔軟な機構が備わっています。演算子を再定義できてしまいます。+の定義を除外してカーネルをインポートし、独自に再定義した上記のペアノの算法での加算に入れ替えます。驚いたことにモジュールTestのスコープ内では+演算子はペアノの算法の加算として機能します。
defmodule Test do
import Kernel, except: [+: 2]
import Peano
def foo(a,b) do
a+b
end
end
iex(9)> Test.foo([[]],[[[]]])
[[[[]]]]
iex(10)>
終わりに
Elixirの柔軟な機能に驚きました。Lispでは演算子を再定義することは容易いことですが、まさかアルゴル系言語のElixirでこんなことができるとは思いませんでした。どういう使い道があるのかはわかりませんが、Elixirの底力を垣間見たような気持ちです。
参考文献
「プログラミングElixir」 笹田耕一、鳥井雪 共訳 Ohmsha