[comp.lang.eiffel] Note on subsequent long message <>

bertrand@eiffel.UUCP (Bertrand Meyer) (04/02/89)

	The message posted after this one, entitled ``Eiffel types''
and numbered <>, is a draft of an article on the
Eiffel type system, which will be adapted as a part of the 2.2 documentation.
The present message explains the purpose of ``Eiffel types'', which is long.

	I hesitated before posting such a long note as ``Eiffel types'' but it
seems that the potential benefits outweighed the standard objections.

	``Eiffel types'' is NOT the article on ``The complete rules for
type checking in Eiffel'' (``Type checking'' for short) which
many people (something like 300) requested after I mentioned it on
the net some time ago. ``Type checking'' complements ``Object-Oriented
Software Construction'' and the Eiffel User's Manual by explaining
the complete rules for Eiffel systems to be typewise correct. ``Type
Checking'' is mostly of theoretical interest; the problems it addresses,
although visible to people who look at Eiffel from an academic point
of view, arise very seldom in practical Eiffel usage.

	In contrast, ``Eiffel types'' deals with very important improvements,
both theoretical and practical, of the type system. These improvements
are part of version 2.2.

	The main theoretical advance is to make the notion
of type in Eiffel completely regular: all types are now classes; the basic
types (INTEGER, REAL, CHARACTER, BOOLEAN) become fully normal classes of
the Basic Eiffel Library. This approach contains the germ of a theory of
types for which (as is appropriate for an area of applied mathematics having
to do with automatic computing devices) the only predefined type is ``bit''.

	The practical advances are numerous: improvements in efficiency through
avoidance of unneeded dynamic allocations and indirections; double precision
reals (or, for that matter, arbitrary precision reals or integers); more
flexible interface with C and other languages; a simple form of operator
overloading; easier bit string manipulation; etc.

	Because ``Eiffel types'' has to do with the type system of Eiffel, I have
decided to merge it with ``Type checking'' but this will take some time.
This is the reason while I have not mailed ``Type checking'' yet to those
who requested it. That will come. Sorry for the delay. Since ``Eiffel types''
opens possibilities of immediate interest for Eiffel users, it appeared
appropriate to post it without delay.

	``Eiffel types'' is posted in <> in nroff-ed form. This is less
than perfect because the original uses italics, bold face, tables
and other typographical niceties. I considered posting the original troff form
but it depends to heavily on our local macro package.

	If there are any follow-ups they will be somehow forwarded to me
but I won't be able to respond soon, since I will be roaming the world
for the next few weeks with little likely access to news.

	Enough apologizing for one message.

-- 

-- Bertrand Meyer
bertrand@eiffel.com

bertrand@eiffel.UUCP (Bertrand Meyer) (04/02/89)

                        EIFFEL TYPES


                       Bertrand Meyer


                           DRAFT


                         March 1989


    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 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  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  names
       INTEGER, REAL, CHARACTER and REAL need no  longer  be
       treated  as  reserved words in the language: instead,
       they  are  names  of  classes  in  the  Basic  Eiffel
       Library. (For practical reasons, however, these names
       will remain reserved in version 2.2.)

    +
      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 dynamic allocation and 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  base
       language.

    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''.

1 THE TYPES


     The type system of Eiffel is entirely based on the notion of
class: every Eiffel type is now defined as a class.

    Most classes will be  defined  by  class  declarations,
under  the syntax and semantics introduced in reference [1].
Since any non-empty class declaration must  refer  to  other
classes (to give the types of attributes, routine arguments,
function results, and to  list  parents  if  an  inheritance
clause  is  present),  some  classes  must  be considered as
predefined.  Eiffel offers an unbounded  set  of  predefined
classes, written

    BITS M

where M is any non-negative integer constant.

    For any M, the definition of class BITS M  is  that  it
describes objects whose representation will fit in M bits.

    In other words, 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 any object that  becomes  associated  with
quadruple at run-time. Of course, the environment is free to
allocate more space.

    Classes BITS M, for any M, are considered to export the
boolean operations: and, or, not, and the constants true and
false. This  makes  it  possible  to  use  these  types  for
performing operations on bit strings of arbitrary length. In
pre-2.2 Eiffel this was done  through  the  library  classes
BOOLEAN_SET and INTEGER_SET, which remain available.

    A predefined inheritance structure exists on the set of
BITS  M  classes:  BITS  M  is  heir  to  BITS M+1. No other
inheritance relation is satisfied by these classes.

2 EXPANDED CLASSES


            In pre-2.2 Eiffel, any entity of a class type,  for  example
an attribute declared as

    ref: CLASS_NAME

where CLASS_NAME is the name of a class, represents  not  an
object but a reference to a potential object.  The object is
``potential''  because  it  is  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 the situation will be
different  if  CLASS_NAME  has  been  declared under a newly
provided  mode,  called  ``expanded''.    The   syntax   for
declaring an expanded class is is

    expanded class CLASS_NAME ... The rest as before ...

As will  be  seen  below,  an  expanded  class  may  not  be
deferred,  so  the  problem  of  the respective order of the
optional keywords expanded and deferred does not arise.

    The effect of declaring a  class  EXP  as  expanded  is
simple: any entity declared of type EXP will represent not a
reference to potential objects of  type  EXP,  but  directly
objects  of  type EXP. (Remember that ``entities'' in Eiffel
cover class attributes, routine arguments, function results,
local routine variables.)

    So if a class C contains an attribute declared as

    sub: EXP

then any instance of C will contain  a  sub-object  of  type
EXP,  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 objects.

    By default, classes are unexpanded.  If a  class  UNEXP
is   not   explicitly   declared   as  expanded,  an  entity
declaration

    ref: UNEXP
keeps its pre-2.2 meaning: ref  denotes  a  reference  to  a
potential object of type UNEXP, to be created dynamically.

    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


    If EXP had not been declared as expanded, attribute sub
would have denoted an attribute which, in any instance of C,
represents a reference to potential instances of EXP.

    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 and dynamic object allocations
may now be avoided.

    In  most  respects,  expanded  classes  have  the  same
properties  as  default  (unexpanded)  classes. An important
difference is that instances of expanded classes do not need
to  be created explicitly. If the type of sub is an expanded
class, the call

    sub.Create

is  still  permitted;  its  sole  effect,  however,  is   to
initialize  the  fields of the object associated with sub to
their default values (based on their types) and then to call
the  specific  Create  procedure  of  the class, if present.
(For unexpanded classes, Create also has these two  effects,
but  it  precedes  them  by  allocating  a new object of the
appropriate type.)

    If C is a composite class (expanded  or  not)  with  an
attribute  sub  of  expanded  type  EXP,  the application of
Create to an instance of C implies that all  fields  of  the
instance  will  be initialized to their default values. This
applies  to  field  sub;   when   performing   the   default
initializations, the Create of C will call the Create of EXP
(default or specific) on this field.

    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.

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

    +

      As any other class, an expanded class 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.

    +

      Expanded classes may  also  be  composite:  in  other
       words,   an   expanded   class  may  itself  have  an
       attributed of expanded type. Among expanded  classes,
       however,  the  relation  ``A has an attribute of type
       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  any  of  these  classes
       contains  a  sub-object  which  is an instance of the
       other.

    +

      An expanded class may  not  be  deferred.   (For  one
       thing, its Create procedure could contain a call to a
       deferred routine. Create is disallowed  for  deferred
       classes anyway.)


3 ASSIGNMENT AND EQUALITY TESTING


            There are three ways of of assigning from b to a  (the  last
one new):

      a := b

      a.Clone (b)

      a.Copy (b)

    The same type rule, described in [1],  applies  to  all
three  forms:  the type of a must be an ancestor of the type
of b. The introduction  of  expanded  classes  brings  in  a
further rule:

    Expanded class type rule: In an assignment from  b

     to  a, if the type of b is expanded, the type of a

     must also be expanded.

    The effect of assignment under its three forms  depends
on whether the type of the left-hand side, a, is expanded or
not.  For an unexpanded type, the rules are the same  as  in
pre-2.2 Eiffel, with the addition of Copy:

      := denotes reference assignment. After the assignment
       a will refer to the same object as b, or will be void
       if b was.

      Clone represents object duplication. If b  refers  to
       an  object, 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.  If either a or b is  void,
       an exception is raised.


     The  copies  performed  by  both  Clone  and  Copy  are
``shallow''  copies;  in  other  words  they copy one entire
object, but that object only,  the  reference  fields  being
copied verbatim.

    For an expanded type, all three  assignments  have  the
effect  just described for Copy. Because of the rules given,
neither a nor b may be void in this case,  so  no  exception
will ever be raised.

    A similar semantics is defined for equality tests.  For
unexpanded  types,  the  boolean  expression a = b denotes a
test for equality of references; the expression  a.Equal (b)
denotes   a  test  for  (shallow)  field-by-field  equality,
returning true if only the  contents  of  the  corresponding
objects are field-by-field identical, or both references are
void. For expanded types, both tests have  the  semantic  of
Equal.


4 ENSURING CONSISTENT SEMANTICS FOR GENERIC CLASSES


            An important design guideline for  generic  classes  follows
from  the preceding specification of assignment and equality
operators.

    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.

    Consider, however, a routine of a generic class, acting
on  values  of generic type; by generic type, we mean a type
defined by a formal generic  parameter  of  the  class,  for
example  T  in a routine of class C [T].  The problem arises
of how programmers can write such a routine and  be  assured
that  it has the same semantics regardless of the types used
as actual generic  parameters  for  practical  uses  of  the
class.

    In light of the above discussion, 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 either Copy rather than :=. Clone
       may also be used if, for generic parameters which are
       unexpanded classes,  duplication  is  desired  rather
       than mere copy; the difference between Clone and Copy
       is irrelevant for expanded classes.


     This rule is particularly important because, as will be
seen  below,  basic  types  (INTEGER,  REAL  and others) are
defined as expanded classes. Using Equal and  Copy  is  thus
the  appropriate  way to guarantee that a generic class will
have uniform semantics  for  basic  types  and  other  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. (Copy did not exist in
pre-2.2 Eiffel.) 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 an
unexpanded  class,  and  value semantics is desired when the
generic parameter is an  expanded  class,  in  particular  a
basic type.

    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.


5 INTERFACE WITH OTHER LANGUAGES


            [Readers who are not interested  in  detailed  questions  of
Eiffel  interface to other languages, particularly C, should
skip to the next section.]

    The notion of expanded class 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  class.  Usually,  EXP
should simply be declared as

    expanded class EXP
     inherit
          BITS M
     end

where M (an integer constant) 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.

    As  an  aside,  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 ARGUMENT PASSING


            The rules for argument passing  are  a  consequence  of  the
above.

    When an Eiffel routine calls  another  Eiffel  routine,
the  semantics  of  argument  passing is the same as that of
assignment from actual  to  formal  argument:  by  copy  for
arguments  of  expanded  types  (including,  as will be seen
below, the basic types), and by reference for  arguments  of
unexpanded types.

    [C-unfascinated readers, please skip to next section.]

    For arguments to external routines, the rule cannot  be
as  systematic;  they  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.


7 PREFIX AND INFIX OPERATORS


            We are now almost ready to explain how basic  types  can  be
interpreted as classes. A syntactical facility, although not
essential, will prove convenient for this.

    To facilitate expressiveness, we allow some routines to
be  used  in prefix or infix form.  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:  +,  -, *, /, <, >, <=, >=; only the first two in
this list are permitted for  a  ``prefix''  declaration.  So
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,  not  just the basic
types.


8 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
Class  NUMERIC  describes  any  type  with  the   basic
arithmetic operations:


     deferred class COMPARABLE 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  connected
by  an  order  relation,  or  elements  on  which operations
similar to the basic arithmetic  operations  are  available,
may  inherit  from COMPARABLE or NUMERIC and offer effective
definitions for the above operations. A class may of  course
inherit from both.

            [Draft note: NUMERIC might inherit from COMPARABLE.]


9 BASIC TYPES AND DOUBLE PRECISION


            We can now  introduce  the  basic  types  as  classes.  This
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.)

    The basic types are defined as follows.


     expanded class REAL export

          infix "+", infix "-", infix "*", infix "/",

          prefix "+", prefix "-",

          infix "<", infix "<=", infix ">", infix ">="

     inherit

          BITS 32;

          NUMERIC;

          COMPARABLE;

          REAL_CONVERTIBLE -- See below

     feature

         infix "+" (other: REAL_CONVERTIBLE): REAL is

                    -- Sum of other and current element

               external

                    short_real_addition (other: REAL): REAL language "..."

               do

                    Result := short_real_addition (other.to_real)

               end; -- "+"

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

         to_real: REAL is

                    -- Conversion to REAL type (here, identity)

               do

                    Result := Current

               end; -- "+"

    end -- class REAL
So we  consider  that  a  (short)  real  number  is  an
expanded  32-bit  object,  which  has the properties of both
``numeric'' and ``comparable'' objects.  The 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.

    The class REAL_CONVERTIBLE is used to account for mixed
arithmetic  operations.   What is added to a real number may
be not just a real  but  also  an  integer.   The  class  is
simple:


     deferred class REAL_CONVERTIBLE export

          to_real

     feature

         to_real: REAL is

                    -- Conversion of current element to a real value

               deferred

               end; -- to_real

    end -- class REAL_CONVERTIBLE

This class is needed because it seems inappropriate  to
make  INTEGER  an  heir  of REAL; assignment, in particular,
would not have its usual meaning. (In version 2.2,  however,
it will remain possible to assign an integer value to a real
entity, with the expected effect of a conversion. This is an
exception  to  the  generality  of the type scheme presented
here, for compatibility with programmers' current habits.)

    Class INTEGER is declared in a way similar to REAL:

    expanded class INTEGER export

          infix "+", infix "-", infix "*", infix "/",

          prefix "+", prefix "-",

          infix "<", infix "<=", infix ">", infix ">="

     inherit

          BITS 32;

          NUMERIC;

          COMPARABLE;

          REAL_CONVERTIBLE

     feature

         infix "+" (other: REAL_CONVERTIBLE): REAL is

                    -- Sum of other and current element

               external

                    integer_addition (other: REAL): REAL language "..."

               do

                    Result := integer_addition (other)

               end; -- "+"

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

         to_real: REAL is

                    -- Conversion to REAL type

               external

                    integer_to_real: REAL language "..."

               do

                    Result := Current.integer_to_real

               end -- to_real

    end -- class INTEGER

Here no conversion is required for integer operations.

    The other basic types are defined similarly.  CHARACTER
is  an  expanded class that inherits from BITS 8. BOOLEAN is
an expanded class that inherits from BITS 1.

    The type DOUBLE, new with 2.2, is formally a  class,
very  similar  to  REAL,  but inheriting from BITS 64 rather
than BITS 32.

    Note that REAL is not an heir  of  DOUBLE.  As  with
integers  and  reals,  an  explicit  conversion mechanism is
required; this does not, however, prevent the  usual  mixed-
mode expressions.

    If longer precision reals or integers are  needed,  the
technique  is  easily  generalized:  just define new classes
that inherit from NUMERIC, COMPARABLE, and BITS  M  for  the
appropriate M.


10 UNEXPANDED CLASSES AND GENERICITY


            Two further conventions are needed to fully  understand  the
Eiffel type system.

    +

      Any unexpanded class (this includes  all  classes  of
       pre-2.2  Eiffel)  is implicitly considered to inherit
       from BITS 32. This is a result of  an  assessment  of
       the  current  state of hardware technology. Instances
       of  unexpanded   classes   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; and (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  of  a
       session.  Note  that  this  convention  is not really
       ``wired  in''   and   some   future   standardization
       committee  might decide to raise the bound to 64 (for
       example)   without   major   impact    on    existing
       applications,   provided   the  same  committee  also
       decided that INTEGER and REAL now inherit  from  BITS
       64 rather than 32.

    +
      Unconstrained genericity, as in a  declaration  of  a
       class   C  [T],  is  taken  as  an  abbreviation  for
       constrained genericity of the form C [T -> BITS  32].
       In  other  words, the default is 32; this covers both
       the short basic types  and  all  unexpanded  classes.
       Again, the value 32 might need to be adjusted in line
       with advances in technology.

    The last convention explains how generic classes may be
written  to  support double precision reals (for example). A
generic class declared as

    MATRIX [T]

will be applicable to  the  actual  generic  types  INTEGER,
REAL, CHARACTER, BOOLEAN or any unexpanded class. It may not
be used for the type DOUBLE, however, because this  class
is not a descendant of BITS 64. To make the class applicable
to double precision reals, declare it as

    MATRIX [T -> BITS 64]

This will work for DOUBLE, and will still  work  for
the  preceding  types;  remember that BITS M is heir to BITS
M+1.

    As another consequence of the above  rules,  a  generic
class  may  usually  not  use  an  expanded  class as actual
generic parameter. More precisely, if  EXP  is  an  expanded
class  and  C is generic, C [...EXP...] is legal if and only
if C has been declared as

    MATRIX [...T -> EXP1...]

where EXP1 is an expanded class, and EXP is a descendant  of
EXP.


11 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
     Copy
     expanded
     infix
     prefix

From a theoretical viewpoint, four have  been  removed:
INTEGER,  REAL,  BOOLEAN,  CHARACTER.   If  we  accept  that
DOUBLE would have been needed anyway, the  reserved  word
count  is  unchanged  by this discussion.  (The names of the
basic types will, however, remain reserved  in  release  2.2
for practical reasons.)

    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  xxx''?);  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 prefix and infix 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   and
multiple inheritance seemed to provide such a powerful basis
for the discussion of types that  it  was  sad  to  have  to
abandon  them  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 class, 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 classes 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,  The  complete  rules  for  static  typing  in
Eiffel, unpublished.
-- 

-- Bertrand Meyer
bertrand@eiffel.com