PyPy Advent Calendar 21日目(失敗編) - RPython と型とか

PyPy Advent Calendar 21日目です!
なんかよくわかりませんが人が少ないので三周目な [twitter:@shomah4a] です。

今回は RPython における型についてです。
型推論あたりの話は 17日目 に [twitter:@cocoatomo] さんが書いていましたが、おおよそあんな感じです。

具体的には

  • なんとなく型推論してくれる
  • 継承関係とか見てくれる

ってところでしょうか。

これを踏まえて簡単な関数を例に型に関する話をしてみます。

C++ を例に出しているけど参照とかポインタとか総スルーなのは許してネ

C++ と比較してみる

RPython での開発はなんとなく C++ 的です。

例えば以下のような関数を定義したとします。

def add(x, y):
    return x + y

引数を二つ取って足して返す関数です。

これを以下のように呼んだ場合、

add(1, 2)

C++ で考えるとこのように解釈されるでしょう。

const int add(const int x, const int y)
{
    return x + y;
}

ただ、 RPython における add 関数は必ずしも整数のみを受け取るわけではありません。
例えば

add(1.0, 2.0)

のように実数値を渡すこともあります。
このように、 RPython における関数の型は、そもそも引数の型定義が存在しないため多相型であると言えます。

実際に add 関数が呼ばれるフローの中で、 int, float 等の違う型の部分があったとしてもそれぞれ正しく推論してくれます。

ですので、 C++ においては

template <typename T>
const T add(const T x, const T y)
{
    return x + y;
}

という解釈になると思われます。

が、 RPython においては

add(1, 2.0)

のように第一引数と第二引数の型が違ったりすることもあるかもしれません。

ここまでを反映すると、 C++ では以下のように書けるでしょう。

template <typename T, typename U>
decltype(std::declval<T>() + std::declval<U>()) add(const T n, const U m)
{
    return x + y;
}

まあここまでやれば十分なのですが、 RPython におけるモジュールレベルで定義される変数は全て定数として扱われます。
その定数定義において add 関数を使用することもでき、

SOME_CONST = add(1, 30.0)

このように使った場合、この add 関数は constexpr であると言えます。
なので、この関数は

template <typename T, typename U>
constexpr decltype(std::declval<T>() + std::declval<U>()) add(const T n, const U m)
{
    return x + y;
}

というところに落ち着くのではないでしょうか。

と思ったら

とか何とかやって多相型で推論してくれるじゃーんとか思っていたら

add(1, 2)
add("aaa", "bb")

で、文字列加算しようとしたところで AnnotationError 出たので多相型じゃないということが発覚してしまいました(´・ω・` )
というのがイマココ。

Rational な型だけなのか…?

なのでこの話はなかったことにっ

この仕様だと継承関係のない型に同じ処理を適用するために関数コピペして名前変えて型ごとに適用とかかなーり悲しい事になりそうな予感…。

なんだかスゲー悲しい。

なので
> 実際に add 関数が呼ばれるフローの中で、 int, float 等の違う型の部分があったとしてもそれぞれ正しく推論してくれます。
というのは正しくないですね、って話でした。

でもあれだよ、なんとなく C++ っぽい感じなのはわかってくれる人もいると思うよ。

というわけで失敗編でした。