[comp.lang.eiffel] Eiffel types

bertrand@eiffel.UUCP (Bertrand Meyer) (07/30/90)

The following article, posted originally in June of 1989 (after
a draft was posted in April of that year) is being reposted since
it addresses some of the most frequently asked questions about the
Eiffel language, and is not widely available.

Four important comments about the reposting:

1. As noted in the original message, this is again an article
meant for typesetter output, and pure character format does
not do it full justice.

2. The article has not been edited at all. This means any error
in the original is still there. Also, this article predated
the book ``Eiffel: The Language'', made available (as an internal
report from Interactive) in August of 1989. That book is the official
reference on the language; if you find a discrepancy, believe the
book, not this article. The article does adequately cover the spirit,
if not always the letter, of the type system, and many users have
told us that it does a better job at explaining the general picture
than the book, which is too dry and analytic. (I have worked to correct
this deficiency of the book, of course, but the result is not yet
available.)

3. Both the book in its current form and this article (with the previous
reservations) apply to the currently available version, 2.2,
available since August 1989, as well as to the forthcoming version, 2.3.

4. There has been further thinking on some of the solutions described
in the article, such as the ``repeat'' mechanism; some recent comments
in this newsgroup (see contributions by Steve Tynor,
Rick Jones and others) pointed out limitations of this mechanism.
Obviously the discussion is not closed.

This reposting should not be very useful to current Eiffel users,
for whom the material described is old and well-known; it should,
however, provide a better perspective to readers whose only exposure
to Eiffel dates back to older versions of the language. 



                          EIFFEL TYPES

                         Bertrand Meyer

                           March 1989

              Updated, 14 May 1989 and 2 July 1989




1 OVERVIEW


This note presents a unified  view  of  the  Eiffel  types.   The
language  as  described  is what is available for version 2.2 and
includes some extensions over the definition given  by  reference
[1].  No incompatibility is introduced; any Eiffel class that was
legal according  to  [1]  remains  legal,  with  the  same  exact
semantics.

     The  conceptual  framework  and  extensions  presented  here
introduce  a  remarkable number of benefits, both theoretical and
practical:

     + Independently of any actual change to  the  language,  the
       concept  of  type  in Eiffel is presented in a clearer and
       more uniform way.  All  Eiffel  types  are  now  based  on
       classes,  and  multiple inheritance is the basic mechanism
       for constructing new classes.  This  means  in  particular
       that  basic types (integer, real, character and real) need
       no longer be treated according to special rules. In  fact,
       the  reserved  words  INTEGER, REAL, CHARACTER and BOOLEAN
       are theoretically no longer needed, as it is now  possible
       to   define  the  corresponding  types  fully  within  the
       language, based  on  new  library  classes.  (These  names
       remain  in  use,  however,  for reasons of convenience and
       efficiency.)

     +  Double  precision  reals,  which  were   not   previously
       supported  directly  in  Eiffel because of theoretical and
       practical problems relating in particular  to  genericity,
       are   now   available.  In  fact,  any  precision  can  be
       supported.

     + Limited support is offered  for  programmer-defined  infix
       and prefix operators.

     + Programmers can improve the efficiency of  their  software
       by  writing classes whose instances include other objects.
       Up to release 2.1, objects could refer  to  other  objects
       through  reference  fields,  but  could  not contain other
       objects.    The   new    possibility    avoids    unneeded
       indirections.

     + Exchange of data  between  Eiffel  software  and  external
       routines  is  made  easier, particularly when the external
       routines are written in  C.  Eiffel  objects  may  contain
       sub-objects which the C world views as C structures.

     +  Operations  on  bit   sequences   of   arbitrary   length
       (previously  supported  by  the library class BOOLEAN_SET)
       are handled simply within the language proper.

     Throughout the rest  of  this  note,  ``Eiffel''  refers  to
version  2.2  Eiffel. The language defined by [1] and implemented
as version 2.1 is called ``pre-2.2 Eiffel''.



2 EXPANDED TYPES


The most important new concept is that of  expanded  type,  which
makes  it possible to declare entities that denote objects rather
than references to objects. As a result, it is  now  possible  to
create ``composite'' objects - objects which contain sub-objects.

     ``Entities''  in  Eiffel  cover  class  attributes,  routine
arguments, function results and local routine variables.


2.1 Overview


In pre-2.2 Eiffel, an entity declaration is of the form:
     ent: TYPE

where  TYPE  is  either  a  class  type  (possibly   defined   by
``association'') or one of the basic types INTEGER, REAL, BOOLEAN
and CHARACTER. (In a generic class, TYPE can  also  be  a  formal
generic  parameter,  representing  either a class type or a basic
type depending on the choice of actual parameter.)

     If  TYPE  is  one  of  the  basic  types,  ent  at  run-time
represents a value of the corresponding type (an integer, a real,
a boolean, a character).  But if TYPE is a class type, the  above
declaration  means  that  the run-time denotations of ent will be
not objects but references to potential objects.  The objects are
``potential'' because they are only allocated explicitly, through
a Create operation. Until it becomes associated with an object, a
reference is said to be ``void''.

     This remains true by default, but  it  is  now  possible  to
declare  an  entity as being of an ``expanded'' type.  The syntax
is:
     ent: expanded CLASS_NAME

     In such a case, ent will be said  to  be  expanded   (as  an
abbreviation   for   ``of  expanded  type'')  and  of  base  type
CLASS_NAME.

     With such a declaration, ent will represent not a  reference
to  potential  objects  of type EXP, but directly objects of type
EXP.

     So if a class C contains an attribute declared as
     sub: expanded D

then any instance of C will  contain  a  sub-object  of  type  D,
accessible  through  attribute  sub.  A class such as C having at
least one attribute of expanded type is called a composite class;
its  instances  are called composite objects. Figure 1 pictures a
composite object.

     If an entity is not explicitly  declared  as  expanded,  for
example
     ref: E
it keeps its pre-2.2 denotation  of  a  references  to  potential
objects of type E.

     Assuming class  C  contains  both  of  the  above  attribute
declarations,  the  structure  of  an  instance  of this class is
illustrated on figure 1.


        --------------------

    ref |                  |  ---> To dynamic instances

        --------------------       of UNEXP

        |                  |

        |                  |

        |      Object      |

    sub |      of type     |

        |       EXP        |

        |                  |

        |                  |

        --------------------

        |                  |

        --------------------
  Other |                  |

        --------------------
 fields |                  |

        --------------------

        |                  |

        --------------------


                 Figure 1: Composite object



     Composite objects serve to model external objects that  have
non-simple  components.   The  modeling  of  such objects was, of
course, possible in  pre-2.2  Eiffel,  but  only  by  making  the
objects   contain   references   to  their  components,  not  the
components themselves.  Now it is possible to  specify  that  the
objects are composite, so that they actually contain sub-objects.
The improvement is not functional -  no  data  structure  can  be
described  which  could not have also be modeled in pre-2.2 - but
is one of performance: useless indirections may now be avoided.


2.2 Create, Void and Forget


Consider an entity declared  of  class  type  (that  is  to  say,
unexpanded):
     ent: C

     The rules of the language (in 2.2 as well as pre-2.2) define
the  effect  of  a  creation operation ent.Create (...) to be the
following sequence of steps:

1
     + Create a new instance of C.

2
     + Associate this instance with ent.

3
     + Initialize its fields, each of  which  corresponds  to  an
       attribute of the class.

4
     + If C has a specific Create procedure, apply it to the  new
       instance, using the actual arguments given if any.

     Now assume that ent is declared as expanded:
     ent: expanded C

Then the call ent.Create is still correct. Here steps 1 and 2 are
meaningless  since ent denotes an instance of C, not a reference.
So the effect of the Create is now limited to  applying  steps  3
and 4 to the object associated with ent.

     In all cases the effect of step 3  (initialization)  depends
for   each  field  on  whether  the  corresponding  attribute  is
expanded. For a non-expanded attribute, the  field  represents  a
reference  and  is always initialized to a void reference. For an
expanded attribute sub, the effect of the  initialization  is  to
apply the call

     sub.Create

to the field. Because sub is expanded, this means applying  steps
3  and  4:  initialization  of all fields, and application of the
specific Create if present. Note that step 3 is recursive because
the type of sub might contain expanded attributes.

     Two other predefined Eiffel features, Void and  Forget,  are
also  allowed  on  entities  of expanded types. If sub is such an
entity, sub.Void always  returns  false  and  sub.Forget  has  no
effect.


2.3 Restrictions


Three simple rules apply to expanded types as  a  result  of  the
above   discussion.   Violation  of  these  rules  will  lead  to
compile-time errors.

     The first rule applies to the Create procedure of  the  base
classes  of  expanded  attributes.  Consider a declaration of the
form
     sub: E

As any other class, class E may or may not have a specific Create
procedure.   If  it has one, however, this procedure may not have
any arguments.  This clearly required in light of  the  rule  for
Create:  in  the  above example, there is no way to initialize an
instance of the composite class C if the  initialization  of  its
expanded sub component requires specific information.

     The second rule prohibits C from being deferred.   (For  one
thing,  its  Create  procedure could contain a call to a deferred
routine. Create is disallowed for deferred classes anyway.)

     The third rule prohibits expansion cycles.  A class used  as
base  for an expanded type may also be composite: in other words,
E in the above example may itself have an attributed of  expanded
type.  But  the  relation  ``class  A  has  an  attribute of type
expanded B'' may not contain any cycles. The reason for this rule
is  obvious:  we  cannot  implement A and B in such a way that an
instance of both of these classes contains a sub-object which  is
an instance of the other.

     Observance of these  three  restrictions  is  necessary  and
sufficient  to  ensure  that  an expanded declaration is correct.
This is summarized in the following rule.

        _________________________________________________


         Expanded rule: An entity  may  be  declared  of
         type  expanded  C  if and only if the following
         three conditions are met:

         1. Class C either has no Create  procedure,  or
         its Create procedure has no argument.


         2. Class C is not deferred.


         3. The declaration does not introduce any cycle
         in the relation between classes defined to hold
         between any two classes X and Y if and only  if
         X has an attribute declared of type expanded Y.

        _________________________________________________


2.4 The BITS types


An unbounded set of predefined expanded types  turns  out  to  be
convenient  for several purposes.  These types are written BITS M
where M is any non-negative integer constant.

     For any M,  the  definition  of  type  BITS  M  is  that  it
describes  values  whose  representation will fit in M bits.  All
BITS types are expanded types.

     By declaring an entity such as
     quadruple: BITS 128

the programmer is requiring the supporting Eiffel environment  to
devote  at  least  128  bits  to the representation of the object
associated with quadruple at run-time. Of course, the environment
is free to allocate more space.

     The  BITS  types  are  essentially  useful   for   low-level
manipulations  of bit strings, for machine-dependent definitions,
and  for  interfacing  with  other  languages.   The   last   two
applications are particularly significant:

     + Classes such as INT, FLOAT and DFLOAT (used to define  the
       corresponding  expanded  types)  will  be defined below as
       classes with an attribute of the  form  value: BITS M  for
       some  M.  The  default M is 32 for INT or FLOAT and 64 for
       DOUBLE.  Thanks  to   the   BITS   classes   the   machine
       dependencies  can  be  recognized  and  isolated  in a few
       classes such as these.

     + Pre-2.2 Eiffel made it possible to exchange  data  between
       Eiffel  and  other  languages  such  as  C,  but  the sole
       interface unit was the word (32 bits by default).  Now  by
       declaring  entities of type BITS M for arbitrary M, Eiffel
       software can send and receive data of any  size.  This  is
       explained in more detail below.

     The operators and, or, xor and not (defined in  infix  form,
as  explained  in the next section) are applicable to entities of
BITS M types.  This makes it possible  to  use  these  types  for
performing  operations on bit chains of arbitrary length. In pre-
2.2 Eiffel this was  done  through  the  kernel  library  classes
BOOLEAN_SET  and INTEGER_SET. These classes provide for variable-
size bit chains and remain available.

     When one of the operators and, or, xor and not is applied to
operands  of  types  BITS M and BITS N, the type of the result is
BITS max (M, N).

     For assignments involving  the  BITS  types,  the  following
rules apply:

     + A value of type BITS M may be assigned  to  an  entity  of
       type BITS N for M <_ N. Only the first M bits of the target
       are affected by such an assignment.

     + Values true and false may be assigned to an entity of type
       BITS M, resulting in the assignment of a chain of all ones
       or all zeros, respectively.

     + No other assignment involving BITS operands is permitted.

     These conventions are consistent with the use of BITS  types
for low-level manipulations.



3 ASSIGNMENT, EQUALITY AND ARGUMENT PASSING


The introduction of expanded types makes it necessary to  specify
precisely  the  kind of association which occurs between entities
and objects as a result of assignment and argument  passing,  and
the meaning of equality tests.


3.1 Overview


Assignment will be considered first.

     Three kinds of assignment are needed to cover  all  possible
cases:  assignment  of  references; object duplication; field-by-
field object copy without duplication.

     When all three forms are available  on  a  source  b  and  a
target  a,  they are expressed by the following respective syntax
forms (the last one new):

     + a := b

     + a.Clone (b)

     + a.copy (b)

     Clone is written with an initial upper-case because it is  a
predefined  language  feature and hence a reserved word. The name
copy, however, is not reserved; instead, copy is a routine of the
new universal class ANY, automatically inherited by every class.

     The effect is determined by whether the target a is expanded
or not.


3.2 Assignment to unexpanded target


If the type of the target a is unexpanded (that is to say, it  is
a  normal  class  type),  then  a  denotes references to possible
objects and all three forms make sense. The rules  in  this  case
are  the same as in pre-2.2 Eiffel, with the addition of copy and
the possibility of an expanded source.

     The effect of the three forms of assignment is as follows:

     + := denotes reference assignment. If b is  unexpanded,  the
       assignment  will  result in a referring to the same object
       as b, or being void if b was. If b is unexpanded,  a  will
       be a reference to the object denoted by b.

     + Clone represents object duplication. If  b  refers  to  an
       object (which is always the case if its type is expanded),
       the Clone operation creates a new copy of this object, and
       makes a refer to it. If b was void, a becomes void.

     + copy represents copy without allocation. The  contents  of
       the  object  associated  with b are copied onto the object
       associated with a.  Both a and b must be non-void.   Since
       copy is a normal feature, from class ANY, applying it to a
       void a will raise an exception. The routine has not b.Void
       as  a  precondition;  in  precondition  checking  mode, an
       exception will be raised if b is void (which cannot  occur
       if its type is expanded).

     As in pre-2.2 Eiffel, the assignment in  any  of  its  three
forms,  is permitted if and only if the base type of b ``conforms
to'' the type of a.  Roughly speaking, this means that the latter
is a direct or indirect heir of, or identical to the former.  The
precise definition of conformance is given in section  11.3.1  of
[1].


3.3 Assignment to expanded target


Now consider the case in which the target a is expanded.  In this
case,  a  directly  denotes an object; as a consequence, the only
semantics that makes sense  is  field-by-field  copy.   Then  all
three syntax forms will have this effect.

     The source b may be expanded or not. If it is expanded,  the
object denoted by b will be copied onto the object denoted a.  If
it is not expanded, b denotes a reference, which must be non-void
for  the  assignment  to  proceed  correctly.  If  so, the object
referred to by  this  reference  is  copied  to  a.  If  not,  an
exception  will result (as when a feature access b.f is attempted
and b is a void reference).

     In this case exact type identity rather than conformance  is
required:  the  base  type of the source must be identical to the
base type of the target,  with  the  extra  tolerance  introduced
above for BITS types.


3.4 Argument passing


An entity may become associated with a value as a result not just
of  an assignment of one of the above kinds, but also of argument
passing.

     The rule for Eiffel routines  is  that  if  a  is  a  formal
argument  to  a  routine,  a  call  to the routine using b as the
corresponding  actual   argument   always   produces   the   same
association between a and b as an assignment
     a := b

     The semantics of argument  passing  then  follows  from  the
above  discussion:  for  unexpanded  a,  the  effect  is pass-by-
reference; for expanded  a,  the  effect  is  pass-by-copy.  Type
conformance  applies  in  the former case, identity in the latter
case. If a is expanded and b is not, the value of b must be  non-
void at execution time, otherwise an exception will be raised.

     This rule applies to Eiffel routines. The rule for  external
routines  (routines  written in other languages) are given in 5.1
below.


3.5 Equality testing


Corresponding to the different kinds of assignment, two forms  of
equality testing may be needed, written respectively:

1
     + a = b

2
     + a.Equal (b)

     The rule is the following. If both a and b  are  unexpanded,
the  form 1 expresses equality of references and form 2 expresses
field-by-field object equality. If both a  and  b  are  expanded,
both  forms  express  field-by-field  object  equality. If one is
expanded and the other is not, both tests are illegal and will be
rejected by the compiler.


3.6 Deep copy and deep equality


The copies performed by Clone and copy, and by := for an expanded
target,  are  ``shallow''  copies;  in  other words they copy one
entire object, but that object only, the reference  fields  being
copied  verbatim.  Similarly, field-by-field equality as provided
by Equal, and by = for expanded arguments,  is  a  shallow  test,
comparing only first-level fields.

     The universal class ANY includes a procedure deep_copy which
will  duplicate  an  entire data structure, and the corresponding
deep_equal function.


4 THE TYPE SYSTEM

4.1 Basic types


The behavior of entities  of  expanded  types,  as  it  has  been
defined,  generalizes  the  behavior  of  entities of basic types
(INTEGER, REAL, BOOLEAN, CHARACTER) in pre-2.2 Eiffel.  For these
basic  types,  assignment  (:= and Clone) had the exact semantics
discussed in the previous section for expanded types.

     The introduction of expanded types indeed makes it  possible
to  cease  treating the basic types as special. Instead, they are
just predefined expanded types.

     More precisely, the Kernel Eiffel Library now includes fully
normal  classes INT, FLOAT, BOOL, CHAR and DFLOAT whose instances
are  objects  representing  integer,   floating-point,   boolean,
character and double precision values.  (The last one corresponds
to a new facility.) The classes in question will be  given  below
(section 6.4).

     To obtain the effect of basic types  in  pre-2.2  Eiffel  it
suffices to declare entities of respective types
      expanded INT;
      expanded FLOAT;
      expanded BOOL;
      expanded CHAR;
      expanded DFLOAT

     The  reserved  words  INTEGER,  REAL,  BOOLEAN,   CHARACTER,
complemented  by  DOUBLE,  subsist  in 2.2 Eiffel, where they are
simply considered to be syntactical abbreviations for the  above.
The  semantics  of  entities  declared  of  any of these types is
exactly the same as in pre-2.2. Of course,  the  Eiffel  compiler
knows  about  these  types  and is able to treat them without any
loss of performance.


4.2 A complete picture of Eiffel types


The above makes it possible to give a full definition  of  Eiffel
types.

     Any Eiffel type is based  on  a  class  but  may  be  either
expanded or unexpanded. An unexpanded type is also called a class
type.

     A class type is defined  by  a  class  name,  possibly  with
actual generic parameters (see below).

     An expanded type is of the form expanded T,  where  t  is  a
class  type.  Expanded types also include INTEGER, REAL, BOOLEAN,
CHARACTER and DOUBLE, which  are  syntactical  abbreviations  are
explained above, and BITS M for any non-negative integer M.



5 INTERFACE WITH OTHER LANGUAGES


[This  section  is  only  for  readers  interested  in   detailed
questions of Eiffel interface to other languages, particularly C.
Others should skip to the next section.]


5.1 Passing arguments to external routines


The semantics of argument passing for Eiffel routines  was  given
above; it is the same as that of := assignment.

     For arguments to external routines, the rule  cannot  be  as
systematic;  it  must  of  necessity  be  adapted  to  the target
language. Current C, for example, does  not  support  passing  of
structures or arrays as arguments; only pointers to such elements
may be passed. (ANSI C may be more liberal,  but  has  yet  shown
little  relevance  to  the real world of C programming.) So for C
all arguments, whether expanded or not, are  normally  passed  by
reference.  It  is  the responsibility of the target C routine to
copy any structure or array if  needed.  An  exception  is  made,
however,  for  arguments  of basic types (integer, real, boolean,
character and the new type ``long real'' described below),  which
are passed by value for compatibility with tradition.

     For the results of external functions, the  Eiffel  compiler
can  take  care  of  ensuring  the  proper  semantics  (copy  for
expanded,  reference  for   unexpanded);   a   language-dependent
convention  is  needed,  however,  to determine what the function
will return. In C, the answer is the only  portable  one:  the  C
function  is  assumed  to return a pointer to the result for non-
basic types, expanded or not.  The code generated by  the  Eiffel
compiler  then  takes  care  of performing a copy in the expanded
case.

     An  important  practical  caveat  governs  the  exchange  of
arguments  and  function results between Eiffel and C (or another
language). We may usually assume that the basic types are  common
to  both  languages,  and hence that values of these types may be
manipulated by both  sides.  Both  C  and  Eiffel  routines,  for
example,  may  perform  additions  on  a given integer value. For
values of any other type,  however,  only  one  side  may  safely
execute  operations  other than parameter passing; the other side
will simply be used as repository for the values, or to pass them
along  to  further  elements.  There are exceptions to this rule,
but they apply to special cases and require that  the  programmer
be   well  versed  in  the  implementation  techniques  for  both
languages.


5.2 Exchanging data between Eiffel and other languages


The notion of expanded type also provides  for  a  more  flexible
interface  with  the  non-Eiffel world. In pre-2.2 Eiffel, Eiffel
routines that needed to interface with routines written in  other
languages,  such  as  C,  could only pass them arguments of basic
types or of class types; in  the  latter  case  the  argument  is
actually  a  reference.  This  also  applied  to the types of the
results of external functions. Furthermore, it was  not  possible
for  an Eiffel object to contain a sub-object modeled directly to
a C structure type  declaration,  although  this  constraint  was
somewhat relieved by the presence of a routine copy_structure, in
the basic library class INTERNAL, making it possible to copy  the
contents of a C structure into an Eiffel object.

     This concern is now addressed by composite classes. If  each
instance  of  C  must  contain a sub-object whose structure comes
from external C software, then  C  should  contain  an  attribute
declaration of the form
     sub: EXP

where EXP is an appropriate expanded type.

     Often, EXP will simply be BITS M, where the integer constant
M  is large enough to cover the size of instances of the required
C structure type.

     It would usually be imprudent to give  EXP  a  more  precise
Eiffel  structure,  since  this would require making non-portable
assumptions about the internal structure of  both  Eiffel  and  C
objects. The above, however, will be sufficient in practice since
entities of type EXP,  such  as  sub,  should  only  be  used  as
arguments  to external function calls. The purpose is not to have
Eiffel do the job of C or the reverse,  but  only  to  facilitate
communication between the two worlds.

     This facility is  consistent with  the  Eiffel  approach  to
interfacing  with  other  languages,  based  on two observations.
First, reusability implies that Eiffel software must be  able  to
interface  with existing software written in other languages, but
not that the Eiffel language should be corrupted by compatibility
with  older, unrelated designs. A passage is provided through the
border, but it remains a border. Second (for the special case  of
C):  C  plays  with  respect  to  Eiffel  the  role that assembly
languages played for earlier  high-level  languages:  that  of  a
low-level vehicle to be used only when one cannot do otherwise.

     On a related topic,  note  that  release  2.2  provides
     further  support  for more powerful interaction between
     Eiffel and other languages.  In  particular,  a  simple
     syntactical  extension  makes  it  possible to pass the
     address of an Eiffel routine to  an  external  routine.
     The  notation  is  @f,  where  f  is  a  routine of the
     enclosing class; such an expression is  only  valid  as
     actual argument to an external routine.



6 DEFINING THE BASE CLASSES FOR THE BASIC TYPES


It was explained above that basic types are expanded types, whose
base classes are new kernel library classes.  These basic classes
will now be introduced.

     The purpose of these definitions is to show that the  Eiffel
type  system, entirely based on the notion of class, is complete:
basic types may entirely be defined within the language and  have
no more ``magical'' properties.

     In practice, however, the basic types will  continue  to  be
treated  in a special way by the compiler for obvious performance
reasons.  The classes as given below define the semantics of  the
basic   types   (each  of  which  is  an  expanded  form  of  the
corresponding class), but are not used as such  by  the  compiler
and   hence  do  not  limit  the  efficiency  of  the  associated
operations.


6.1 Prefix and infix operators


The classes underlying the  basic  types  use  the  facility  for
defining routines by infix and prefix operators, as introduced by
2.2.

     The standard grammar for the declaration  of  routines  (see
[1],  appendix  C)  is  as  follows  (brackets introduce optional
components:


___________________________________________________________________


 Routine_declaration   =   Routine_name [Formal_arguments] [Type_mark]
                           "is" Routine_text
    Formal_arguments   =   "(" Entity_declaration_list ")"
           Type_mark   =   ":" Type

___________________________________________________________________




     The optional ``Type_mark'' gives the type of the result when
the routine is a function.

     In pre-2.2 Eiffel, ``Routine_name'' is simply an identifier.
We now allow the two new forms
     prefix '"'Operator'"'
     infix '"'Operator'"'

     In  this  syntax,  ``Operator''  may  only  be  one  of  the
following:
     +  -  *  /  <  >  <=  >=
     and  or  xor  not
     div  mod


(Operator xor, for exclusive or, is new with 2.2.)

     All these except not may be used in infix declarations; only
+, - and not are permitted for a prefix declaration.

     Thus only a small group of common operators may be  used  in
prefix or infix form.

     Routines declared in this way must all be  functions.  Infix
routines  must  have  exactly  one argument; prefix routines must
have none.  Calls to such routines  must  use  the  corresponding
operators, in prefix or infix form.

     Assume for example a class MATRIX with function declarations
of the form
     infix ("+") (other: like Current): like Current is...

     prefix ("-"): like Current is...

Then, for m1 and m2 of type MATRIX, the expressions m1 +  m2  and
-m1,   respectively,  will  denote  calls  to  the  corresponding
functions.


     The precedence of infix and prefix operators  is  fixed  and
given by the standard Eiffel precedence table (see section C.3 of
[1]).

     It is important to note that the above  possibility  is  not
``the  introduction of overloading into Eiffel''. A powerful form
of overloading has always been available in Eiffel, since any two
classes can have different features of the same name, and dynamic
binding makes it possible to obtain run-time discrimination. What
is new is a simple syntactic extension, making it possible to use
the standard arithmetic operators for any class  (and  associated
expanded types), not just the basic types.


6.2 COMPARABLE and NUMERIC


Two deferred classes are needed in the basic Eiffel library.

     Class COMPARABLE already existed in  pre-2.2;  its  features
now  use  the  infix  facility.  Note  that only "<=" needs to be
deferred.
     deferred class COMPARABLE export
          infix "<", infix "<=", infix ">", infix ">="
     feature

          infix "<=" (other: like Current): BOOLEAN is
                    -- Is current element less than or equal to other?
               deferred
               end; -- "<="
          infix "<" (other: like Current): BOOLEAN is
                    -- Is current element less than other?
               do
                    Result := Current < other and not Current.Equal (other)
               end; -- "<"
          infix ">" (other: like Current): BOOLEAN is
                    -- Is current element greater than other?
               do
                    Result := other < Current
               end; -- ">"
          infix ">=" (other: like Current): BOOLEAN is
                    -- Is current element less than or equal to other?
               do
                    Result := other <= Current
               end -- ">="
     end -- class COMPARABLE

     Any class that needs to manipulate elements connected by  an
order relation may inherit from COMPARABLE.

     Class NUMERIC describes any type with the  basic  arithmetic
operations:
     deferred class NUMERIC export
          infix "+", infix "-", infix "*", infix "/",
          prefix "+", prefix "-"

     feature

          infix "+" (other: NUMERIC): NUMERIC is
                    -- Sum of other and current element
               deferred
               end; -- infix "+"

          prefix "-" BOOLEAN is
                    -- Is current element less than other?
               deferred
               end; -- prefix "-"

          (etc.)
     end -- class COMPARABLE

     Any  class  that  needs  to  manipulate  elements  on  which
operations   similar  to  the  basic  arithmetic  operations  are
available, may inherit  from  NUMERIC  (and  from  COMPARABLE  if
necessary).


6.3 Conversions


To allow for mixed-mode  computation,  we  will  need  conversion
functions.   For  example,  the  following class describes values
which may be converted to floating-point:
     deferred class FLOAT_CONVERTIBLE feature

          float_value: FLOAT is
                         -- Representation of current object as a floating-point value
                    deferred
                    end -- float_value
     end -- class FLOAT_CONVERTIBLE

     A  similar  DFLOAT_CONVERTIBLE  class  may   be   used   for
conversions to double precision.


6.4 Basic types and double precision


We can now introduce the base classes for the basic types.

     The definition is based on  an  assessment  of  the  present
state  of  computer  hardware:  on  most platforms, an integer or
(short) real will fit in 32 bits; a character will fit in 8 bits;
a long real will fit in 64 bits.

     I believe it is preferable to explicitly  incorporate  these
industry-standard   lengths   into   the   programming   language
definition,   rather   than   to   maintain   a    pretense    of
implementation-independence. In practice such a pretense can only
prevent software developers from writing portable  software  that
will  perform in a (reasonably) consistent fashion across a range
of  platforms.   (Another,  more  axiomatic  approach  has   been
followed  by  Ada,  but  its advantages over the policy of making
simple assumptions about the  minimum  bit-size  requirements  of
basic  data  types are not clear in the present state of hardware
technology and software theory.)

     These conventions are not really  ``wired  in''  and  it  is
possible  to  change  the definitions below to adapt to different
sizes.

     The basic classes are defined as follows.
     class FLOAT export
          repeat NUMERIC, repeat COMPARABLE, value {FLOAT}
     inherit
          NUMERIC;

          COMPARABLE;

         FLOAT_CONVERTIBLE
     feature

          value: BITS 32;
                    -- The real number's internal representation
          set_value (v: BITS 32) is
                    -- Use v as the bit code for the real number's value
               do
                    value := v
               end; -- set_value


          float_value: FLOAT is
                         -- Representation of current object as a floating-point value
                    do
                              -- Here of course no conversion is necessary
                         Result := Current
                    end; -- float_value
          infix "+" (other: FLOAT_CONVERTIBLE): FLOAT is
                    -- Sum of other and current element
               external
                    short_real_addition (v, w: BITS 32): BITS 32 language "..."
               do
                    Result.Create;
                    Result.set_value (short_real_addition (value, other.float_value))
               end; -- "+"

          ... And similarly for other prefix and infix operations ...

     end -- class FLOAT

     So we consider that a short real number is an  expanded  32-
bit   object,  which  has  the  properties  of  NUMERIC  objects,
including those of COMPARABLE objects.

     The repeat notation which appears in the export  clause  for
FLOAT is a facility (new for 2.2) which makes it possible to copy
conceptually the export clause  of  an  ancestor  class  such  as
COMPARABLE  and  NUMERIC  into a descendant such as FLOAT without
copying it physically.  (In addition, value is  exported  to  the
class itself.)

        _________________________________________________


         The repeat facility addresses  a  problem  that
         was  mentioned  by  users  who  developed large
         systems with pre-2.2 Eiffel: the tediousness of
         managing  export  lists  when  many  levels  of
         inheritance are involved.  The  problem  should
         now  disappear;  if  you  specify with a repeat
         clause that the interface of a class  D  should
         always  include  the  interface  of  one of its
         ancestors A, then any addition to A's interface
         will automatically be reflected in D.

        _________________________________________________

     The  basic  operations  of  class   FLOAT   operations   are
implemented through external routines, since these operations are
to be carried out  directly  by  the  hardware  (or  through  the
lower-level   implementation   language   used   by   the   given
implementation of Eiffel, such as assembly or C).  The idea  here
is the same as with the way arrays are treated in pre-2.2 Eiffel.
Arrays,  and  now  real  numbers,  are  viewed  as  instances  of
``normal''  Eiffel classes in the Basic Library. From a practical
standpoint, however, the implementation  knows  about  these  and
implements  the  corresponding  operations  through shortcuts: no
actual routine call is needed to access an array element,  or  to
add  two reals. So the usual efficiency of fundamental operations
is achieved, but  the  conceptual  framework  (classes,  objects,
inheritance) remains consistent with the rest of the language.

     Class INT is declared in a way similar to FLOAT:

     class INT export
         repeat NUMERIC, repeat COMPARABLE, value {INT}, to_real
     inherit
         NUMERIC;

         COMPARABLE;

         FLOAT_CONVERTIBLE
     feature

          value: BITS 32;
                    -- The integer's internal representation
          set_value (v: BITS 32) is
                    -- Use v as the bit code for the integer's value
               do
                    value := v
               end; -- set_value
          float_value: FLOAT is
                         -- Representation of current object as a floating-point value
               external
                    int_to_float (v: BITS 32): BITS 32 language "..."
               do
                         Result.Create;
                         Result.set_value (int_to_float (value))
               end; -- float_value
          infix "+" (other: INT): INT is
                    -- Sum of other and current element
               external
                    integer_addition (v, w: BITS 32): BITS 32
                    language "..."
               do
                    Result.set_value (integer_addition (value, other.value))
               end; -- "+"

          ... And similarly for other prefix and infix operations ...

     end -- class INT


     The other basic  types  are  defined  similarly.  For  CHAR,
attribute  value  is  of  type BITS 8; for BOOL, value is of type
BITS 1.

     The  class  DFLOAT  describing  double-precision  reals  and
serving as base class for the expanded type DOUBLE, new with 2.2,
is almost identical to FLOAT but uses BITS 64 rather than BITS 32
for the declaration of value and the associated entities.

     If longer  precision  reals  or  integers  are  needed,  the
technique  is  easily  generalized:  just define new classes that
inherit from NUMERIC and COMPARABLE and have  an  internal  value
attribute of the right BITS size.



7 GENERICITY


It  is  possible  in  Eiffel  to  write  general-purpose  classes
describing  ``container''  data  structures (such as lists, trees
etc.) containing objects of arbitrary types. These are written as
generic  classes.   Genericity  is  essential to reconcile static
typing with the need for reusable container classes.

     The preceding discussion raises some new issues with respect
to genericity.


7.1 Form of generic declarations


A generic class is defined with one or more  generic  parameters,
as T in
     class C [T, ...], ...

     Variations on the way  T  may  be  specified  are  described
below.

     An entity declared in the  class  as  being  of  type  T  or
another generic parameter will be said to be of generic type.

     Then declarations of entities of types based on C require an
actual generic parameter, such as ACTUAL in
     x: C [ACTUAL, ...]

     ACTUAL is an arbitrary type.  It may be either a class  type
or  an expanded type (of the form expanded BASE, with the special
cases corresponding to the basic types, or BITS M).


7.2 Ensuring consistent semantics for generic parameters


Consider a routine r of a generic  class,  acting  on  values  of
generic  type T.  The problem arises (both in pre-2.2 and current
Eiffel) of how the author of the class can write r  so  that  the
routine  has  the same semantics regardless of whether the actual
generic parameter corresponding to T is expanded or not.  class.

     In light  of  the  discussion  on  assignment  and  argument
passing  in  section  3  above,  the  answer  is clear. Only copy
semantics is available for both expanded  and  unexpanded  types.
(For  expanded  types  reference  semantics does not make sense.)
Writers of generic classes may thus guarantee  uniform  semantics
by making sure that:

     + All equality tests between expressions of generic type use
       Equal rather than =.

     + All assignments use copy rather than :=. Clone may also be
       used  if,  for  generic  parameters  which  are unexpanded
       types, duplication is desired rather than mere  copy;  the
       difference  between  Clone  and  copy  is  irrelevant  for
       expanded types.

     This rule is  particularly  important  because  basic  types
(INTEGER  etc.)  are defined as expanded. Using Equal and copy is
thus the appropriate way to guarantee that a generic  class  will
have uniform semantics for basic types and class types.

     It is important to note that this  was  essentially  already
true  in pre-2.2 Eiffel: as a first step towards the more general
solution described here, Equal and Clone were  already  available
on  basic  types,  with  the  respective semantics of = and := on
these types.  The rules described  above  simply  generalize  and
systematize these conventions.

     Clearly, the above guidelines should  only  be  followed  if
uniform  semantics  is  desired;  they  do not mean that = and :=
should be banned from  generic  classes.  One  can  conceive   of
perfectly  valid generic classes for which reference semantics is
desired when the actual generic parameter is a  class  type,  and
value  semantics  is  desired  when  the  generic parameter is an
expanded type, in particular one of the basic types.

     Since field-by-field object comparison and object  copy  are
the  operations that make sense in all cases, it is legitimate to
criticize the syntax retained: why are the simpler symbols (= and
:=)  used  for  operations  whose  semantics  is not the same for
expanded and unexpanded types, and more verbose notations (Equal,
copy) used for the operations which have consistent semantics and
hence appear ``cleaner''?

     This is a valid criticism. Indeed, I  seriously  considered,
during  the  initial  design  of  Eiffel,  making  := denote copy
assignment and =  denote  object  equality.  But  this  idea  was
rejected  out of fear that such a clash with the tradition of all
previous languages supporting reference semantics  (Pascal,  Ada,
PL/I,  and  many others) would result in numerous mistakes on the
part of new Eiffel programmers,  not  all  of  whom  may  yet  be
expected to learn Eiffel as their first programming language.

     The relative clumsiness of the syntax for the  more  uniform
operations  was  deemed  less  unpleasant  than  the  prospect of
massive mistakes by the cohorts of new converts, still recovering
from the influence of older programming languages.

     As explained in section 5.8.3 of [1], using variants of  the
same  notation  for  copy  and  reference  semantics (such as the
traditional operators := and = for copy, and the Simula operators
:-  and  ==  for  reference)  would  have been the worst possible
solution, implying that minor syntactical  or  typing  oversights
could  result in drastic changes of meaning. The notations had to
be visibly different. New reserved  words  (Clone,  copy,  Equal)
were deemed clearer than new operators.


7.3 Catering to generic parameters of various sizes


One more technique is needed  to  benefit  the  full  benefit  of
expanded  classes.  We need to be able to define generic classes,
representing such abstractions as matrices, in such  a  way  that
the  formal  generic  parameters  can  be  expanded types such as
DOUBLE representing objects of any given size.

     Consider the following general class:
     class MATRIX [T -> NUMERIC] export
          repeat NUMERIC
     inherit
          NUMERIC
     feature

          ... Matrix representation and operations,
          expressed in terms of the corresponding operations on
          objects of type T ...

     end -- class MATRIX


     This generic class will accept any unexpanded type as actual
generic  parameter  corresponding  to  T.  It  will  also  accept
expanded types such as BOOLEAN, CHARACTER, INTEGER and REAL whose
``size'',  defined  below, is less than 32. It will not, however,
accept DOUBLE or other ``big'' expanded types.

     To describe matrices of bigger composite  objects,  you  may
define  a  different  class,  an heir to MATRIX with a very short
definition such as:
     class D_MATRIX [T (DOUBLE) -> NUMERIC] export
          repeat MATRIX
     inherit
          MATRIX                   -- No need for a feature clause
     end -- class D_MATRIX

     The new facility here is the possibility to specify  a  type
(DOUBLE  in  this example) as a bounding type on a formal generic
parameter (T in this example).  A bounding  type  is  written  in
parentheses  after  the  generic  parameter.   If  none  is given
explicitly, the bounding type is taken to be BITS 32 by default.

     The effect of having  a  bounding  type  associated  with  a
formal generic parameter T is given by the following two rules:

     +  The  size  (as  defined  below)  of  any  actual  generic
       parameter  corresponding  to  T must be no bigger than the
       size the bounding type.

     + If C is a type involving a generic class  G  and  D  is  a
       descendant  class, the rules for conformance between D and
       C (governing  assignment  and  parameter  passing  between
       these  two types) are complemented by the requirement that
       the bounding types for  each  generic  parameter  must  be
       exactly the same in both cases.

     The size of a type is defined as follows:

     + The size of all  class  types  is  the  same.  It  can  be
       referred  to as the size of the universal ancestor ANY. By
       default this is 32.

     + The size of a BITS M expanded type is M.

     + The size of any other expanded type expanded C is obtained
       by  adding the sizes of the types of all the attributes of
       class C (those defined in  the  class  itself,  and  those
       inherited from ancestors).

     From the above definitions, then, the size  of  INTEGER  and
REAL is 32 and the size of DOUBLE is 64.

     Class D_MATRIX above should be  used  to  describe  matrices
whose  elements  are no bigger than DOUBLE objects. This includes
any class type as well  as  BOOLEAN,  CHARACTER,  INTEGER,  REAL.
Note   that   the   conformance   rule  precludes  such  improper
combinations as assignment of an integer matrix or  a  matrix  of
references to a double-precision matrix.

     The convention that the size of ANY and other class type  is
32  results  from  an assessment of the current state of hardware
technology. values of unexpanded  types  are  accessible  through
references,   that  is  to  say,  at  the  implementation  level,
pointers. We thus accept that, for still some time:

1.
     + Limiting Eiffel applications to manipulate 2 ^ 31 (about  two
       billion)   dynamic  objects  during  a  given  session  is
       acceptable

2.
     + Computer memory is not  cheap  enough  that  it  would  be
       preferable to raise this limitation to, say, 2 ^ 63 , if this
       implied  doubling  the  space  occupied  by  the  pointers
       generated during a session.



8 CONCLUSION


The  mechanisms  described  above  may  at  first  appear  as   a
significant  extension  of pre-2.2 Eiffel. They are not. Instead,
the  work  reported  here  should  primarily  be  viewed   as   a
reorganization  and  cleanup  of the theoretical framework, which
makes important improvements available at the cost of very little
actual  extension.  As  a matter of fact the above discussion has
introduced only five new reserved words:
     BITS
     DOUBLE
     expanded
     infix
     prefix

     From  a  theoretical  viewpoint,  four  notions  have   been
removed:  the  basic  types  INTEGER,  REAL,  BOOLEAN, CHARACTER.
DOUBLE would have been needed anyway.

     The starting point for this  work  was  both  practical  and
theoretical.  From  a practical viewpoint, I had known for a long
time that it would be ultimately indispensable to support  double
precision;  however double precision initially appeared extremely
difficult in the presence of genericity. Clearly I did  not  want
an  implementation  of genericity which would result (as most Ada
compilers do for generic packages) in duplicating code  for  each
separate  use  of  a generic class; this was deemed unacceptable.
At some point I realized that, from a practical perspective,  the
mechanism   of   constrained  genericity  (implemented  for  2.1)
contained the key. But then why stop at double precision?  Eiffel
strives  for  generality;  what  if  someone  wants, say, 128-bit
reals?

     Another practical concern, which had been present  for  some
time,  was to avoid dynamic allocation whenever possible. In most
applications, the overhead of pointers is acceptable; whether  in
Eiffel,  Pascal,  Ada  or C, most non-trivial data structures are
accessed  through  pointers  anyway.  Still,  this  overhead   is
annoying  for  applications that manipulate very large numbers of
composite objects each of which contains several small  but  non-
atomic   components   -  say  triangles,  each  containing  three
``point'' attributes.

     Yet another motive was the desire to improve the flexibility
of  the  interface  of Eiffel with other languages, especially C.
This requirement is not  an  internal  one,  since  for  our  own
developments  the  external interface of pre-2.2 Eiffel, based on
external routines that essentially pass BITS  32  values  to  and
from  Eiffel, is more than enough. Eiffel users have increasingly
requested support for more general interface mechanisms, however;
the aim in particular is to facilitate cooperation between Eiffel
software  and  existing  packages  (graphics,  databases,  expert
systems  etc.)  The  support  for  composite  objects  should  be
particularly useful here, since it covers a need often voiced  by
users,   that   of  having  Eiffel  objects  contain  sub-objects
described by C structures.

     Support for prefix and infix operators was never by itself a
major  concern.   In  fact  I  was  (and remain) strongly against
unbounded operator overloading, which opens the door to all kinds
of   notational   abuses;  I  am  much  more  interested  in  the
semantically useful overloading  permitted  by  redefinition  and
dynamic  binding than by purely syntactical techniques which make
it possible to call a two-argument routine under the form a @#$ b
(say).  The ability to define the precedence of new operators, in
particular,  has  always  struck  me  as  a  perfect  example  of
programming  language  feature  that  is bad on all counts: it is
difficult to find a good syntax for it (do you specify precedence
by  a  number,  forcing each program writer or reader to refer to
the programming language reference manual each time,  or  do  you
say ``one less than the precedence of operator x''?); it makes it
particularly easy to write tricky programs; it invites  bugs;  it
makes  the  parser - normally the most trivial and worriless part
of a modern compiler - considerably more difficult  to  implement
(since  each program may change the syntax); and to top the whole
thing,  it  is  only  a  superficial,   syntactical,   ``vanity''
extension.  Not the most urgent feature to add to Eiffel.  It was
clear, however, that if we were to treat basic types formally  as
classes (see next), then we should be able to interpret the basic
operators as denoting routines. Why not then make them  available
to  writers of normal programmer-defined classes, such as MATRIX,
VECTOR and the like? Since the concept would only apply to a  few
predefined  operators,  the  extension  would  remain  small  and
reasonable.

     I also had more  theoretical  concerns.  The  difference  of
treatment  between basic types and class types in pre-2.2 Eiffel,
explained in chapter 5 of [1], came directly from Simula. I  knew
that  some  difference  was necessary: in most cases, class types
need reference semantics, and  usually  they  also  need  dynamic
object  creation,  but  no  one  would  want to allocate integers
dynamically.  Still,  this  inconsistency  was  unpleasant  in  a
language  that emphasizes simplicity and uniformity. The concepts
of class  seemed  to  provide  such  a  powerful  basis  for  the
discussion  of  types that it was sad to have to abandon it for a
handful of basic types - the most fundamental at that.

     When  I  presented  Eiffel  concepts  in   public   courses,
listeners  would  often  question  the special treatment of basic
types, asking why INTEGER, for  example,  was  not  a  class.  My
answer  usually  included (among other arguments) that any theory
of types needed to start somewhere, and  thus  to  assume  a  few
pre-existent,  ``Bourbaki-given''  types.  Why  not, the argument
would go, use for these the few types  with  which  everybody  is
familiar,  and  which  are  readily  available on all hardware? I
would usually say something like ``In  the  most  purist  object-
oriented  approach, we would still need to take at least one type
for granted: the bit''.

     In a way, the  framework  presented  above  implements  this
purist approach, although it considers not a single bit type, but
an unbounded sequence of bit types, BITS M.  Everything  else  is
derived  from these. Thanks to the notion of expanded type, which
also addresses  the  problems  that  I  have  called  ``practical
concerns'',  the  basic types make perfect sense in this context;
they  cease  to  be  special  language  elements   with   magical
properties  and  become  standard  library  classes.  To make the
solution clear and uniform, limited support for infix and  prefix
operators,  useful  in its own right, may be introduced.  No loss
in performance is implied because the compiler can be made  smart
enough to recognize these classes and apply special techniques to
them; in fact, the pre-2.2 implementation of basic types  remains
entirely valid. Finally, the introduction of expanded types makes
it possible to tackle the dual-semantics issue  in  a  much  more
explicit,  complete,  and  (I  hope) convincing fashion than ever
before.

     I feel that the general cleanup of the Eiffel type system is
almost  complete  with  the  discussion  of this article (and the
companion discussion [2], which addresses details  of  the  rules
making  Eiffel  fully  statically  typed  through  constraints on
polymorphism, not well covered in [1] and not yet fully supported
by  the  implementation).   To  make  the  basic  type into fully
``ordinary'' citizens, we would also need to deal  with  manifest
constants,  which  could  be  treated  as  ``zerofix'' operators.
Example of such operators would be 0 and  1,  now  introduced  as
deferred functions in class NUMERIC, corresponding to the neutral
elements for operations  infix  "+"  and  infix  "*".  The  other
integers  would  be  interpreted as syntactical abbreviations for
1+1, 1+1+1 etc. Such an extension would require that we  be  more
formal,  separating  what  has been called NUMERIC into different
inheritance levels (GROUP, RING, FIELD etc.) characterized by the
proper  assertions. This is worth exploring further; there may be
the root of a full type theory, perhaps even of a  general  model
for  computation.  This  extension,  however, will not be part of
Eiffel release 2.2.

References

[1] B. Meyer, Object-Oriented  Software  Construction,  Prentice-
Hall, 1988.

[2] B. Meyer,  Static  typing  in  Eiffel,  Interactive  Software
Engineering Inc., Technical Report TR-EI-18/ST, January 1989.


Added reference (July 1990): ``Eiffel: The Language''; Technical
Report TR-EI-17/RM, Version 2.2, August 1989. To be published by
Prentice-Hall in 1990.