erg/doc/JA/compiler/trait_method_resolving.md
Cai Bingjun 37abfb36d3 trifle
2022-09-11 20:03:06 +08:00

4.5 KiB

パッチメソッドの解決

badge

Natは0以上のInt、つまりIntのサブタイプである。 本来NatはPythonのクラス階層には存在しない。Ergはこのパッチのメソッドをどうやって解決するのだろうか?

1.times do:
    log "hello, world"

.timesNatImplのパッチメソッドである。 1Intのインスタンスであるので、まずIntのMRO(Method Resolution Order)を辿って探索する。 ErgはIntのMROにInt, Objectを持っている。これはPython由来である(Pythonにおいてint.__mro__ == [int, object])。 .timesメソッドはそのどちらにも存在しない。ここからは、そのサブタイプの探索に入る。

~

整数は明らかにその上位型に実数や複素数、ひいては数全体を持つはずだが、Pythonと互換性をもつレイヤーではその事実は現れない。 だが実際にErgでは1 in Complex1 in NumTrueとなる。 Complexに至っては、Intと継承関係にないクラスであるのに、型として互換性があると判断されている。一体どうなっているのか。

~

あるオブジェクトに対して、その属する型は無数に存在する。 だが実際に考えなくてはならないのはメソッドを持つ型、すなわち名前を持つ型のみである。

Ergコンパイラは、全ての提供メソッドとその実装を持つパッチ・型のハッシュマップを持っている。 このテーブルは型が新たに定義されるたびに更新される。

provided_method_table = {
    ...
    "foo": [Foo],
    ...
    ".times": [Nat, Foo],
    ...
}

.timesメソッドを持つ型はNat, Fooである。これらの中から、{1}型に適合するものを探す。 適合判定は二種類ある。篩型判定とレコード型判定である。篩型判定から行われる。

篩型判定

候補の型が1の型{1}と互換性があるか確認する。篩型の中で{1}と互換性があるのは、{0, 1}, 0..9などである。 0..1 or 3..4, -1..2 and 0..3などの有限要素の代数演算型は、基底型として宣言すると篩型に正規化される(つまり、{0, 1, 3, 4}, {0, 1, 2}にする)。 今回の場合、Nat0.._ == {I: Int | I >= 0}であるので、{1}Natと互換性がある。

レコード型判定

候補の型が1のクラスであるIntと互換性を持つか確認する。 その他、Intのパッチである、またその要求属性をIntがすべて持つ場合も互換性がある。

~

というわけで、Natが適合した。ただFooも適合してしまった場合は、NatFooの包含関係によって判定される。 すなわち、サブタイプのメソッドが選択される。 両者に包含関係がない場合は、コンパイルエラーとなる(これはプログラマーの意図に反したメソッドが実行されないための安全策である)。 エラーを解消させるためには、パッチを明示的に指定する必要がある。

o.method(x) -> P.method(o, x)

全称パッチのメソッド解決

以下のようなパッチを定義する。

FnType T: Type = Patch T -> T
FnType.type = T

FnTypeパッチのもとで以下のようなコードが可能である。これはどのように解決されるのだろうか。

assert (Int -> Int).type == Int

まず、provided_method_tableにはFnType(T)が以下の形式で登録される。

provided_method_table = {
    ...
    "type": [FnType(T)],
    ...
}

FnType(T)のパッチする型が適合するかチェックされる。この場合、FnType(T)のパッチ型はType -> Typeである。 これはInt -> Intに適合する。適合したら、単相化を行って置換する(T -> TInt -> Intのdiffを取る。{T => Int})。

assert FnType(Int).type == Int