Edited at

GADTを使って、より安全なアドホック多相関数をHaxeで実現する

More than 5 years have passed since last update.

Haxeの多相関数の型推論についてではHaxeでアドホック多相関数を実現しているが、addメソッドの引数に異なる型の値が与えられた場合でもコンパイルが通ってしまうので、実行時例外で対処している。せっかくHaxeは静的型付けの言語であるのだから、どうせならコンパイル時にエラーが発覚してほしい。GADTを使えばそれが実現できる。

まず、enum AddをGADTを使って定義する。


Add.hx

enum Add<X> {

Plus(i : Int) : Add<Int>;
Concat<T>(a : Array<T>) : Add<Array<T>>;
}

implicitな変換が効くようにAddを包むAAddを定義する。これはnobkz氏の方法と同様。


AAdd.hx

abstract AAdd<X>(Add<X>) {

inline function new(x) this = x;
@:from public static inline function fromInt(x : Int) : AAdd<Int>
return new AAdd(Plus(x));
@:from public static inline function fromArray<T>(x : Array<T>) : AAdd<Array<T>>
return new AAdd(Concat(x));
@:from public static inline function fromAdd<T>(x : Add<T>) : AAdd<T>
return new AAdd(x);
@:to public inline function toX() : X
return switch(this) {
case Plus(i): i;
case Concat(a): a;
}
@:to public inline function toAdd() : Add<X>
return this;
}

GADTを使っているので、toXメソッドで正しくない型に変換されるおそれがなく、実行時例外を起こす必要がない。

そして、addメソッドを定義する。


Addition.hx

import Add;

class Addition {

public static function add<X>(x : AAdd<X>, y : AAdd<X>) : AAdd<X> {
return switch(x) {
case Plus(x): switch(y) {
case Plus(y): Plus(x + y);
}
case Concat(x): switch(y) {
case Concat(y): Concat(x.concat(y));
}
}
}

}


不格好に見えるが、パターンマッチを2段階にして行わないと現状ではコンパイルが通らない。(haxe GADT + tuple bug)

実際に使ってみよう。


Main.hx

class Main {

public static function main() {
var i : Int = Addition.add(1, 2);
trace(i);
var a : Array<String> = Addition.add(["a", "b"], ["c", "d", "e"]);
trace(a);
// var x = Addition.add(1, ["a"]);
}
}

実行結果:

Main.hx:4: 3

Main.hx:6: [a,b,c,d,e]

7行目のコメントアウトを外すと、当然コンパイル時エラーになる。

./Main.hx:7: characters 26-31 : Array<String> should be AAdd<Int>

./Main.hx:7: characters 26-31 : For function argument 'y'

このように、GADTはとても便利だ。うまく使おう。