Imaginary Template Metaprogramming in D

D言語を使って脳内でメタプログラミングして遊んでみる(コンパイルできる環境が手元にないので想像)。本当にただの妄想なので、そこのところは了承してちょんまげ。

template Tuple(xs...) {
    alias xs Tuple;
}

いつの間にかPhobosから削除されていたけど、これがないと不便。

template Foldr(alias f, alias init) {
    alias init Foldr;
}

template Foldr(alias f, alias init, alias x, xs...) {
    static if (xs.length != 0)
        alias f!(x, Foldr!(f, init, xs)) Foldr;
    else
        alias f!(x, init) Foldr;
}

template Foldl(alias f, alias init) {
    alias init Foldl;
}

template Foldl(alias f, alias init, alias x, xs...) {
    static if (xs.length != 0)
        alias Foldl!(f!(init, x), xs) Foldl;
    else
        alias f!(init, x) Foldl;
}

コンパイルタイムfold。
どうせならFoldl!((T, U) { ... }, x, xs)みたいにtemplateリテラル書きたいなぁ、となる。なんかデリゲートテンプレートリテラルはあるらしいけど。でも無名なtemplateがあったりしたら、そのメンバの名前を省略できなくて悲しい、なんかないのか…いや、そこはC++式にtypeメンバを用意するとかでいいのかも知れないけど。

struct Pack(xs...) {};

別のタプルを二つ以上引数に取りたいときどうすればいいかなぁ…と思って考えてみた。型にすればいけるんじゃね?というわけで、

template Foo(T : Pack!(xs).Pack, U : Pack!(ys).Pack, xs..., ys...) {}

なんかこんな感じに書けて、Foo!(Pack!(int, char), Pack!(double, float))と書けばxs, ysが勝手に求まってほしいんだけど、まぁ知らん。私が調べた限りでは、一度に複数のテンプレートパラメータを決定することはできるっぽいし、TemplateTupleParameterを二つ以上書くことは禁止されていない。
せめてtemplate Foo(T : Pack!(xs).Pack, xs...)とかはできてほしいというか、これができないとか何なの?ってなる。
あと一応 .Pack まで書いたけど、いらんのかどうかは知らん。

template Zip(T : Pack!(), U : Pack!()) {
    alias Tuple!() Zip;
}

template Zip(T : Pack!(x, xs), U : Pack!(y, ys), x, xs..., y, ys...) {
    alias Tuple!(Pack!(x, y), Zip!(Pack!(xs), Pack!(ys))) Zip;
}

TとUを与えてx, xs, y, ysが自動推論されるなら、こんな感じにZipを書ける。

template AddReference(T) {
    alias typeof(ref typeof(T.init) function() { return T.init; } ()) AddReference;
}

refが型の一部で、typeof(f())が、f()の戻り値の型そのままを示すのであれば、AddReferenceメタ関数は書ける。ref Tというのは関数のパラメータの型か戻り値の型にしか書けないらしいので、こんなややこしい書き方になった。

template Foo(alias x, xs...) {
    alias x Foo;
}

alias Tuple!(int, int, int) tup;

alias Foo!(int, int, int).x test1;
alias Foo!(Tuple!(int, int, int)).x test2;
alias Foo!(tup).x test3;

って書いたとき、私としてはtest1〜3は全て同じ結果になってほしいのだけど、どうなんだろう…
test1はまぁintになると思うけど、test2の場合、alias xにTuple!(int, int, int)が丸ごとセットされるのか、test1みたいにタプルが解体されて、xがintでxsが残り全部、みたいになるのか。TemplateAliasParameterの説明にはTemplateInstanceを引数に取れるとは書いてないけど、global nameにTemplateInstanceが含まれるかもしれないから(C++ルールでは)、test2はintなの?それともTuple!(int, int, int)なの?test3の場合、引数は明らかにtemplate alias nameなので、結果はTuple!(int, int, int)にならないとおかしい。でもaliasかそうでないかで挙動が変わるのは不審すぎるし、このあたりの挙動が分からんなぁ…Exceptional DとかEffective Dとか出たら間違いなく槍玉にあげられるレベルでやばい。

まぁ細かいことは実装=規格ということでいいんだろうけど、それだといつまでたっても規格決まらないじゃない!遊びにくいですわよ!

で、また思ったんだけど、別に毎回毎回特殊化でパターン書いて判別しなくても、

template Unpack(T : Pack!(xs), xs...) {
    alias xs Unpack; 
}

というのさえ私の意図した通りになってくれればUnpack!(tup)だけで済むので、それはそれでいいんじゃなかろうかと思う。
あとtemplate Foo() { template Foo() { alias int x; } }みたいに、あるテンプレートのすぐ内側に同じ名前のテンプレートが定義できるのだろうかというのも分からん。できたからと言って面白いことができるとか、そういうのは知らん。