4.1 KiB
属性の解決
属性の解決とは、例えばx.y
という式が与えられたときにこの式全体の型を決定することを指します。従ってx
の型を決定する必要がありますが、x
の型は一意に決定できない場合があります。そのような場合でもx.y
の型は決定できる場合がありますし、失敗する場合もあります。これが本項が扱う属性の解決の問題です。
簡単な場合として、1.real
(x == 1, y == real
)という式を考えてみましょう。1
の型は{1}
です。{1}
はNat
やInt
、Obj
の部分型です。これらの型を順番に辿って、real
の定義を探します。この場合はInt
で見つかります(Int.real: Int
)。従ってx
の型はInt
にキャストされ、1.real
の型はInt
となります。
このように、x
の型が一意に特定出来る場合は左辺x
->右辺y
という順番で推論が進みます。
しかしx
の型が特定できないとき、逆にy
からx
の型が絞られることもあります。
例えば、このような場合です。
consts c = c.co_consts
co_consts
はCode
型の属性です。この関数の意味するところは本質ではなく、単に他と被らない名前であるからこの例を選択しました。
c
の型が指定されていないので、一見推論はできないように見えますが、(名前空間中にco_consts
を持つ型がCode
しかないときは)可能です。
Ergでは変数の型が指定されていないとき、型変数が割り当てられます。
consts: ?1
c: ?2
型推論器は?2
型からco_consts
の所属を特定しようとしますが、?2
は何の条件もついていない型変数なので、失敗します。
このような場合、get_attr_type_by_name
というメソッドが呼ばれます。
このメソッドでは、これまでとは逆に、co_consts
という名前から?2
の型を特定しようとします。
これが成功するのは、名前空間中にco_consts
を持つ型がCode
しかないときのみです。
Ergでは関数の型検査はモジュール内で閉じているので、モジュール外でco_consts
を属性に持つ型が定義されていても、そのインスタンスをconsts
関数に渡すとエラーになります(それを可能にするためには、後述するStructural
を使う必要があります)。この制約によってconsts
関数の推論が可能になります。
型推論器は、クラス属性が定義されるとき、その"属性"と"定義クラス、属性の型"のペアを記録しておきます。
co_consts
の場合は{co_consts: {Code, List(Obj, _)}}
というペアです。
method_to_classes: {co_consts: [{Code, List(Obj, _)}], real: [{Int, Int}], times!: [{Nat, (self: Nat, proc!: () => NoneType) => NoneType}], ...}
key-valueペアのvalueが配列になっていることに注意してください。この配列が長さ1であるとき、または(部分型関係による)最小の要素が存在するときのみ、keyは一意に特定できたということになります(そうでなければ型エラーが発生します)。
keyが特定できたら、その定義型を?2
の型に逆伝搬させます。
?2(<: Code).co_consts: List(Obj, _)
最終的に、consts
の型はCode -> List(Obj, _)
となります。
consts(c: Code): List(Obj, _) = c.co_consts