erg/doc/EN/compiler/trait_method_resolving.md
2022-11-08 17:16:47 +09:00

3.4 KiB

Resolving patch methods

Nat is zero or more Int, a subtype of Int. Nat does not exist in the Python class hierarchy. I wonder how Erg solves this patch method?

1.times do:
    log "hello world"

.times is a NatImpl patch method. Since 1 is an instance of Int, it is first searched by tracing the MRO (Method Resolution Order) of Int. Erg has Int, Object in the MRO of Int. It comes from Python (int.__mro__ == [int, object] in Python). The .times method does not exist in either of them. Now let's explore that subtype.

~

Integers should obviously have reals, complexes, and even whole numbers in their supertypes, but that fact does not appear in the Python-compatible layer. However, 1 in Complex and 1 in Num are actually True in Erg. As for Complex, even though it is a class that does not have an inheritance relationship with Int, it is judged to be compatible as a type. What the hell is going on?

~

An object has an infinite number of types to which it belongs. But we really only have to think about types with methods, i.e. types with names.

The Erg compiler has a hashmap of patch types with all provided methods and their implementations. This table is updated each time a new type is defined.

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

Types that have a .times method are Nat, Foo. From among these, find one that matches the {1} type. There are two types of conformity determination. They are refinement-type judgment and record-type judgment. This is done from the refinement type determination.

Refinement type determination

Check if the candidate type is compatible with the type {1} of 1. The refinement types compatible with {1} are {0, 1}, 0..9, and so on. Finite element algebraic types such as 0..1 or 3..4, -1..2 and 0..3 are normalized to refinement types when declared as base types (i.e. {0, 1, 3, 4}, {0, 1, 2}). In this case, Nat is 0.._ == {I: Int | I >= 0}, so {1} is compatible with Nat.

Determine record type

Check if the candidate type is compatible with Int, a class of 1. Others that are patches of Int and that Int has all the required attributes are also compatible.

~

So Nat fit. However, if Foo also matches, it is determined by the containment relationship between Nat and Foo. That is, subtype methods are selected. If there is no containment relationship between the two, a compile error will occur (this is a safety measure against executing a method against the programmer's intention). To eliminate the error, you need to specify the patch explicitly.

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

method resolution for universal patches

Define a patch like this:

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

Code like the following is possible under the FnType patch. I wonder how this will be resolved.

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

First, FnType(T) is registered in provided_method_table in the following format.

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

FnType(T) is checked for matching types. In this case, FnType(T) patch type is Type -> Type. This matches Int -> Int. If it fits, do monomorphization and replace (take a diff of T -> T and Int -> Int, {T => Int}).

assert FnType(Int).type == Int