erg/doc/JA/compiler/trait_method_resolving.md
Shunsuke Shibayama 96132b20f6 initial commit
2022-08-10 23:02:27 +09:00

95 lines
4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# パッチメソッドの解決
`Nat`は0以上の`Int`、つまり`Int`のサブタイプである。
本来`Nat`はPythonのクラス階層には存在しない。Ergはこのパッチのメソッドをどうやって解決するのだろうか
```erg
1.times do
log "hello, world"
```
`.times``NatImpl`のパッチメソッドである。
`1``Int`のインスタンスであるので、まず`Int`のMRO(Method Resolution Order)を辿って探索する。
Ergは`Int`のMROに`Int`, `Object`を持っている。これはPython由来である(Pythonにおいて`int.__mro__ == [int, object]`)。
`.times`メソッドはそのどちらにも存在しない。ここからは、そのサブタイプの探索に入る。
~
整数は明らかにその上位型に実数や複素数、ひいては数全体を持つはずだが、Pythonと互換性をもつレイヤーではその事実は現れない。
だが実際にErgでは`1 in Complex``1 in Num``True`となる。
`Complex`に至っては、`Int`と継承関係にないクラスであるのに、型として互換性があると判断されている。一体どうなっているのか。
~
あるオブジェクトに対して、その属する型は無数に存在する。
だが実際に考えなくてはならないのはメソッドを持つ型、すなわち名前を持つ型のみである。
Ergコンパイラは、全ての提供メソッドとその実装を持つパッチ・型のハッシュマップを持っている。
このテーブルは型が新たに定義されるたびに更新される。
```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}`にする)。
今回の場合、`Nat``0.._ == {I: Int | I >= 0}`であるので、`{1}``Nat`と互換性がある。
## レコード型判定
候補の型が1のクラスである`Int`と互換性を持つか確認する。
その他、`Int`のパッチである、またその要求属性を`Int`がすべて持つ場合も互換性がある。
~
というわけで、`Nat`が適合した。ただ`Foo`も適合してしまった場合は、`Nat``Foo`の包含関係によって判定される。
すなわち、サブタイプのメソッドが選択される。
両者に包含関係がない場合は、コンパイルエラーとなる(これはプログラマーの意図に反したメソッドが実行されないための安全策である)。
エラーを解消させるためには、パッチを明示的に指定する必要がある。
```erg
o.method(x) -> P.method(o, x)
```
## 全称パッチのメソッド解決
以下のようなパッチを定義する。
```erg
FnType T: Type = Patch T -> T
FnType.type = T
```
`FnType`パッチのもとで以下のようなコードが可能である。これはどのように解決されるのだろうか。
```erg
assert (Int -> Int).type == Int
```
まず、`provided_method_table`には`FnType(T)`が以下の形式で登録される。
```erg
provided_method_table = {
...
"type": [FnType(T)],
...
}
```
`FnType(T)`のパッチする型が適合するかチェックされる。この場合、`FnType(T)`のパッチ型は`Type -> Type`である。
これは`Int -> Int`に適合する。適合したら、単相化を行って置換する(`T -> T``Int -> Int`のdiffを取る。`{T => Int}`)。
```erg
assert FnType(Int).type == Int
```