Single-level call rule and generics
Problem
The single-level call rule says that every call has to be valid for all possible dynamic types of an entity. So for a formal generic g
this means that the call has to be valid for all possible descendants of the constraint (or at least for every instantiation which happens in a system).
Let's look at an example using NUMERIC
:
class X [G -> NUMERIC] feature f (g1, g2: G): G do Result := g1 + g2 end end
Here, the call to +
has to be valid for all possible descendants of NUMERIC
. Since the signature of the feature is like Current
, the call is never valid. This has to be the case as otherwise, the following code would produce a catcall:
x: X [NUMERIC] x.f (1, 1.0)
The problem is that the formal generic G
is always assumed to be of any type which satisfies the constraint, so normally the formal generic is polymorph.
As the above example show, the use of features on formal generic parameters which are covariantly redefined is severly limited. But as the purpose of NUMERIC
is exactly to allow creation of generic algorithms over numeric types, this has to be addressed (Note that also COMPARABLE
is a problem).
Possible solutions
Monomorph formal generics
The first idea is to be able to mark a formal generic as monomorph:
class X [monomorph G -> NUMERIC] f (g1, g2: G): G do Result := g1 + g2 end end
The generic class can then only be instantiated with monomorphic types, i.e. using interval types X [INTEGER..INTEGER]
or X [REAL..REAL]
.
For the check we can now assume that G
will be monomorphic. This helps a little bit furhter: For every feature argument which is like Current
we can assume that the exact type will be given. But this works only for covariant features which have like Current
arguments which are never redefined into something else. Take the following classes for an example:
class A feature f (a: like Current) do end end class B inherit A end class C inherit A redefine f feature f (a: D) do end end class D inherit C end
Now assume the following generic class:
class X [monomorph G -> A] feature f local g1, g2: G do g1.f (g2) end end
The call to g1.f
is not valid since G
could be of type C
, and then the argument has to be of type D
. So now, not even the like Current
feature is callable with monomorphic generics. Basically it is the same as the declaration without monomorphic for the generic.
So this solution works only for the case of like Current
features which are never redefined. Only then it can be guaranteed for a monomorphic generic formal, that the signature will match the formal generic exactly. Other covariant features have still to be valid for all possible descendants.
For the case of NUMERIC
and COMPARABLE
the use of monomorphic formal arguments would work as they define only like Current
features which are normally not redefined in subclasses. A more general mechanism would be nice but at the moment it is unclear how such a mechanism should look like.
Remodeling library
Another way to solve the problem of NUMERIC
and COMPARABLE
would be to change the library. As these classes have mostly like Current
arguments and result types, they can be modeled via generics in the first place.
For example:
class NUMERIC [G -> NUMERIC [G]] feature infix "+" (other: G): G end class COMPARABLE [G -> COMPARABLE [G]] feature less_than (other: G): BOOLEAN end
With this approach, the use of the general class NUMERIC
and COMPARABLE
will become more complicated, but since they appear in libraries most of the time it could be done.