We have to distinguish several kinds of overloading. Only some of them are supported by MMT.
Semantic Overloading
Here the same identifier is used for unrelated constants. These usually have different arities or types so that uses of the shared identifiers can be disambiguated. This is very common in programming languages.
This is intentionally not supported by MMT. Instead, MMT is designed such that every declaration has a unique identifier.
When representing languages with semantic overloading in MMT, qualified names such as c/0
, c/1
, … should be used for declarations sharing the overloaded name c
.
In any case, it is still possible to use the same notation for all these declarations and use syntax overloading to emulate semantic overloading in user-facing formats.
Syntax Overloading
Here the same notation is used for unrelated identifiers.
The MMT parser/checker supports syntax overloading. The parser generates all possible syntax trees, and the checker will choose one of them if the type of exactly one of them can be inferred uniquely.
Subtyping Overloading
Here a single semantic operation is formalized as multiple constants of the same name. Contrary to semantic overloading, these constants are related: They have function types with overlapping domains and are implicitly specified to agree on the shared domain.
This is common in algebraic specification languages, e.g., CASL. It is most commonly used for arithmetic functions on the various number domains: For example, addition may be declared for natural numbers and integers.
Type Class Overloading
A type class C
is a set of operations on an abstract type. An instance of C
is a concrete type with corresponding operations on it.
Typical examples are the algebraic structures.
Thus, a constant f
declared in C
is then available for each instance.
This can be seen as a special case of syntax overloading: Every instance of C
creates a new copy of f
, and all copies have different types but the same notation.
Alternatively, one can consider f
as a single operation that takes the specific instance as an additional first argument. In that case, there is no overloading.
If the instance argument a
is explicit, the notation a.f(x)
can be used as in object-oriented programming languages.
If the instance argument is implicit, interpretation must disambiguate in a way that is similar to syntax overloading.
This is not supported yet by MMT.
If multiple symbols use the same notation, the parser returns all possible parses.
oneOf(k, a_1, ..., a_n)
where k
is a natural number and the a_i
are the possible alternatives.
The meaning of this object is a_k
.
After parsing, k is a variable of unknown value.a_i
share their subterms.
This structure-sharing is obtained for free because Scala anyway uses pointers internally.When encountering an ambiguous term, the type-checker chooses the one that is well-typed.
oneOf
tries to infer the type of each a_i
.a_K
, the checker solves k:=K
.k
is solved, the simplification rule for oneOf
rewrites the whole term into a_K
.Due to the presence of other unknown variables, type-checking is usually only semi-decidable. Therefore, it can be very difficult to conclude that an alternative is definitively ill-typed. In particular, it becomes important to have other typing rules that can return a definite No. (Typical rule implementations return either Yes or Maybe, and MMT implements negation as failure.)
This may require some global restrictions on the available rules. For example, a rule trying to prove c=d
for definition-less constants c
and d
can only conclude No if it knows that no other rule can rewrite c
into d
.