Interval types

Research: This page describes research about Eiffel, not the actual language specification.


With interval types, the programmer can restrict the conforming types for an entity. This way, the type set which needs to be checked for system validity of a feature call is restricted to a smaller set of types. In addition, certain rules for generic types will prohibit the unsafe use of generics.


At every location where a normal type is expected, an interval type can be specified by adding an upper bound for each type:


This specifies an interval of all types between the types LOWER and UPPER.


The intention of an interval type is to specify that an entity can only be attached to a type which lies inside the interval. Thus a type declared as ANY..STRING can be attached to an object of dynamic type ANY or dynamic type STRING. It cannot be attached to an object of a type which lies outside this interval, for example INTEGER or PATH_NAME.

Each interval type describes the set of types which belong to the interval. A type belongs to the interval if it conforms to the lower bound (LOWER) and if the upper bound (UPPER) conforms to it.

a: A .. C <=> {A, B, C}
b: A .. B <=> {A, B}
c: A .. NONE <=> {A, B, C, ...}

Conformance rules

An interval type b conforms to an interval type a if it is a sub-interval of a. So if a fully encloses the interval b, b conforms to a.

The conformance rules can also be explained by differently:

  • An interval type b conforms to an interval type a if the set of types described by the interval of b is a subset of the set described by the interval of a.
a := b -- Is valid as {A, B} is a subset of {A, B, C}
  • A type b: C..D conforms to the type a: A..B if C conforms to A and B conforms to D.

Generic conformance is the same as it is now: A generic type b conforms to a generic type a if the base class of b conforms to a and all generic parameters conform as well.

Generic features

For features with formal arguments, another rule exists: A call on a feature which contains a formal argument is only valid if the generic substitution is monomorphic.

This means that a call on a feature f (g: G) of a class X [G] is only valid if G is monomorphic. Thus the type has to be generically derived with X [Y..Y]. So a call to f on a type X [Y..NONE] is not allowed.

Default behaviour

As a shorthand for interval definition, we define the following default behaviour which is different for ordinary types and for actual generic type parameters. Ordinary types are per default polymorphic and actual formal parameters monomorphic:

x: T <=> T .. NONE
y: A [T] <=> A [T .. T] .. NONE


See the introduction to examples for information about the classes used.

Cats and dogs

A feature call on a target t is only valid if it is valid for all polymorphic types which can possibly be assigned to t. With interval types the set of conforming types can be restricted in order to be able to perform calls to features which are covariantly redefined in descendants by excluding such descendants from the dynamic type set.

a_none: ANIMAL <=> ANIMAL .. NONE
c_none: CAT <=> ANIMAL .. NONE
d_none: DOG <=> ANIMAL .. NONE
c_c: CAT .. CAT
d_d: DOG .. DOG
a_c: ANIMAL .. CAT
a_d: ANIMAL .. DOG

Valid assignments:

a_none := c_none
a_none := d_none
a_none := a_a
a_none := c_c
a_none := d_d
c_none := c_c
d_none := d_d
a_c := a_a
a_c := c_c
a_d := a_a
a_d := d_d

Invalid assignments:

-- Invalid: a_a := a_none
-- Invalid: c_c := c_none
-- Invalid: d_d := d_none
-- Invalid: a_c := c_none
-- Invalid: a_d := d_none

Valid calls: (cat_food) (food) (food)

Invalid calls: (food)

A simple generic algorithm

It is easy to declare an algorithm which needs only read-access on a list:

  print_all (a_list: LIST [PERSON..NONE])
      -- Print all attributes of each person in the list.
          -- Reading is valid on a covariant list
      l_students: LIST [STUDENT]       <=> LIST [STUDENT..STUDENT]
      l_professors: LIST [PROFESSOR]   <=> LIST [PROFESSOR..PROFESSOR]
      l_person_printer: PERSON_PRINTER
      create l_person_printer
        -- LIST [STUDENT..STUDENT] conforms to LIST [PERSON..NONE]
      l_person_printer.print_all (l_students)
      l_person_printer.print_all (l_professor)


Generic features

The rule about calling features with formal arguments is too restrictive. Let's look at the following code:

deferred class COMPONENT
  components: LIST [COMPONENT]

What should the type of feature components be? In order to be able to call extend on the list, it has to be monomorphic, thus LIST [COMPONENT..COMPONENT]. But since the argument type is now COMPONENT..COMPONENT we actually cannot add anything to the list as no descendant of COMPONENT is in the interval. Actually, since COMPONENT is deferred nothing can be added to the list at all.

But if we define the list as LIST [COMPONENT..NONE] - thus allowing descendants of COMPONENT in the list - then the rule about features with formal arguments disallows any calls to feature extend as the argument is not monomorphic anymore.

Expanded and Void

Look at the following code snippet:

  any_none: ANY..NONE
  any_string: ANY..STRING
    -- invalid as NONE does not conform to INTEGER
  any_none := 5
    -- invalid as NONE is not in the interval ANY..STRING
  any_string := Void

It illustrates two problems in conjunction with NONE:

  • The first is that NONE does not conform to expanded types. This makes it impossible to declare a type interval which includes all types in the system, reference or expanded. This prevents the programmer from writing code that works with reference types and expanded types, say a generic print algorithm.
  • The second one is that Void is of type NONE. This raises the question how Void should be assigned to an interval type of references, or how such a type is initialized in the first place.


The rule about features with formal arguments must be changed to make this solution usable as otherwise the use of generics is restricted too much.

The problem with Void and NONE can easily be solved by declaring that NONE inherits from all types regardless of reference or expanded status. To solve the problem of assigning Void to an interval type it can be defined that Void is of any (detached) reference type. You could for example say that Void always has the type of the lower bound of the target interval if this type is a (detached) reference type. That way it can be assigned to any interval of a reference type, even a monomorphic one, and it is still disallowed to assign it to expanded types.