概要
Juliaには型の階層があり、構造体を宣言する際にsupertypeを指定することができるが、supertypeになるのは抽象型のみであって、具象型に対してサブタイプを作ることはできない。このため、クラスベースのプログラミング言語で行うような差分コードだけを追加するようなことができない。
ではどうするかというと、supertypeに当たるものをメンバとして保持しておき、関数をフォワードする。しかしたくさん関数があると大変面倒なので、マクロを使う。
やりたいこと
Javaで例をあげる。
class A {
void f0() {System.out.println("A::f0");}
void f1() {System.out.println("A::f1");}
}
のようなクラスに対して、f1
だけ再定義したクラスBを作りたいとする。Javaだと次のように継承して書けばいい。
class B extends A {
void f1() {System.out.println("B::f1");}
}
Juliaでの書き方
ベースとなる構造体Aはこんな感じで書く。
struct A end
f0(a::A) = println("f0(A)")
f1(a::A) = println("f1(A)")
これを再利用して構造体Bを定義するには次のように書く。
struct B a::A end
f0(b::B) = f0(b.a)
f1(b::B) = println("f1(B)")
f0
を再利用するために、型A
を型B
のメンバとして保持しておき、f0
はA
のものを呼出している。関数1つだけなら、こう書くのは簡単だが、再利用したい関数が大量にあったらたまらない。
@forward マクロを使った書き方
このような定型的な定義を生成してくれるマクロがパッケージMacroTools
に用意されている。定義は単純なので自作している人も多数いるようだ。
import MacroTools
@MacroTools.forward B.a f0
とすると、
f0(b::B) = f0(b.a)
が自動生成される。本当は引数のついた大層なものが定義されるようだが。
この例だとなんともありがたみに欠けるが、このマクロは関数名を列挙できるので大量の関数を持つような型でも簡単に差分プログラミングできる。
所感
しかし、なんで具象型のサブタイピングを許さないんだろうか。ほぼ同じ発想でオブジェクト指向を構成しているCLOSではごく普通に許しているし、あまつさえ謎めいた多重継承までサポートしている。そのせいで呼び出される汎関数を実行時に決定するのが大きな負荷になっていたが、このあたりが重くて遅くなるのを嫌ったのか? だとすると性能命のJuliaっぽい。