Single-level call rule and generics

Revision as of 15:51, 13 July 2007 by Juliant (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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.