型のグループ
Pythonでは、特殊メソッド(__eq__, __lt__, __gt__など)を実装することによって、比較演算子を使うことができるようになります。例えば、intはdir(int)
とすると、intの属性が表示されますが、その中に上記の特殊メソッドも含まれており、比較演算子を使うことができるようになっています。
Pythonにはint、strなど型はあります。しかし、Pythonには「この特殊メソッドを実装しているものは〇〇というグループである」というようなものはありません。
なので、作ってみました。
Eq
まず、Eqという__eq__を実装しているグループを作成してみます。
from abc import ABC, abstractmethod
class Eq(ABC):
@staticmethod
@abstractmethod
def __eq__(x, y):
return x == y
特殊メソッド__eq__を実装しているものをEqグループに属しているものとします。
そのために上記のコードにさらに以下のものを追加します。この__subclasshook__を実装すると、__eq__を持っているものはEqのサブクラスと認定され、issubclass関数にてTrueとなります。しかし、これを実装しないと、もともと__eq__を持っている組み込み型のintやstrなどが本当はEqグループに属せるのにissubclass関数ではFalseとなってしまいます。
@classmethod
def __subclasshook__(cls, C):
if cls is Eq:
if "__eq__" in C.__dict__:
return True
return NotImplemented
次にEqのグループに属するものを作成します。
class Int:
def __init__(self, val):
self.val == val
def __eq__(self, other):
return Eq.__eq__(self.val, other.val)
ここでclass Int(Eq)
のような抽象クラスEqを継承するパターンと、registerというメソッドを使い、仮想的サブクラス扱いにするパターンがあります。
今回は後者の方でやっていきます。というのは、予定としてはRustの#[derive(Eq)]
のような形で使えたらいいなと考えているからです。そのためには以下のような関数を作成し、デコレータを使えるようにします。
def derive(*abscls):
def derive_wrapper(cls):
def wrapped(abscls):
return abscls.register(cls)
for a in abscls:
return wrapped(a)
return derive_wrapper
これで、以下のような形で使うことができるようになります。
@derive(Eq)
class Int:
...
Eq以外でも例えば、Ord(<, >などの比較演算子を持つ)なんていうグループもあっても良いだろうし、OCamlのBaseのようにSexpableというS式との変換ができるグループもあっても良いと思います。もし、複数のグループに属する感じにするなら以下のように
@derive(Eq, Ord, Sexpable)
class Int:
...
という書き、それぞれ必須のメソッドを実装すればOK。組み込み型も足りない必須メソッドを追加すればうまくいくはず...