convolution.d

id:rinset さんの D で STL ネタなんかを見てて触発されて、今の D って関数プログラミングサポートを C++ 以上にできる部分もあるんでないかなぁ…とか思って Haskell の標準ライブラリを実装していってみました。

ふと関数合成で止まったんですが、試行錯誤したのち、実装できました。

template rettype_delegate(T, U) {
    T rettype_delegate(T delegate(U));
}
template rettype_function(T, U) {
    T rettype_function(T function(U));
}
template rettype_funcptr(T, U) {
    T rettype_funcptr(T (*)(U));
}
template rettype(T) {
    static if (is (T == delegate)) {
        alias typeof(rettype_delegate(T)) rettype;
    }
    else static if (is (T == function)) {
        alias typeof(rettype_function(T)) rettype;
    }
    else static if (is (T : Object)) {
        alias typeof(rettype_funcptr(&T.opCall)) rettype;
    }
    else {
        alias typeof(rettype_funcptr(T)) rettype;
    }
}

template argtype_delegate(T, U) {
    U argtype_delegate(T delegate(U));
}
template argtype_function(T, U) {
    U argtype_function(T function(U));
}
template argtype_funcptr(T, U) {
    U argtype_funcptr(T (*)(U));
}
template argtype(T) {
    static if (is (T == delegate)) {
        alias typeof(argtype_delegate(T)) argtype;
    }
    else static if (is (T == function)) {
        alias typeof(argtype_function(T)) argtype;
    }
    else static if (is (T : Object)) {
        alias typeof(argtype_funcptr(&T.opCall)) argtype;
    }
    else {
        alias typeof(argtype_funcptr(T)) argtype;
    }
}

template arg(alias A, T) {
    typeof(A(T.init)) arg(T t) {
        return A(t);
    }
}

class convolution_t(T, U) {
    this(T t, U u) {
        t_ = t;
        u_ = u;
    }
    rettype!(T) opCall(argtype!(U) t) { return t_(u_(t)); }
private:
    T t_;
    U u_;
}

template convolution(T, U) {
    convolution_t!(T, U) convolution(T t, U u) {
        return new convolution_t!(T, U)(t, u);
    }
}

unittest {
    assert(convolution(delegate int(int x) { return x+1; }, &toInt)
           ("123") == 124);
    assert(convolution(delegate int(int x) { return x+1; },
                       convolution(delegate int(int x) { return x+1; },
                                   &toInt))
           ("123") == 125);
    assert(convolution(convolution(delegate int(int x) { return x+1; },
                                   delegate int(int x) { return x+1; }),
                       &toInt)
           ("123") == 125);
/* toString is overloaded!
    assert(convolution(&toString,
                       delegate int(char[] s) { return s.length; })
           (1234) == 4);
*/
    assert(convolution(delegate int(char[] s) { return s.length; },
                       &arg!(toString, int))
           (1234) == 4);
}

こんなんできるんか…これたぶん C++ でできんですよね。記憶喪失な私は C++ を思い出せんのですが。

もうちょい色々実装簡単にできそうな気もする。

説明。

序盤の rettype だの argtype はテンプレート引数でのヒント無しに関数ライクなものから引数だの返り値だのを取得するためのもの。 typeof と static if パワーに頼ってる感じです。 C++ だとちゃんと functor として返り値とかを typedef せんとダメだと記憶してます。 operator() の返り値を取る方法が無いというか…いやあったっけ。覚えてない。

arg っていうテンプレート関数は toString みたいに引数でオーバーロードされてる関数の中から特定の関数を選ぶもの。今回の場合は int -> char[] の toString を選んでいます。これはたぶん alias テンプレート引数があるからできるワザで、 C++ じゃオーバーロードされてる関数はにっちもさっちもいかんかった記憶があります。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h