mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-29 12:24:45 +00:00
95 lines
4 KiB
Markdown
95 lines
4 KiB
Markdown
# パッチメソッドの解決
|
||
|
||
`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
|
||
```
|