[comp.lang.eiffel] BASIC TYPES

marcap@doyle.cs.concordia.ca (PAWLOWSKY marc) (02/20/90)

ASSUMPTIONS:

  The definition for INTEGER is int.e
    (It inherits from comparable).

  The compiler handles basic types (INT, STRING, REAL, .. ) specially

THE EXAMPLE

  ARRAYS that can be sorted.

THE PROBLEM

  ec can not handle constraints on basic types.

DISCUSSION

  Creating a constrained generic type is very basic to OOP and Eiffel
  in particular.  However the basic types for 2.2 can not be passed
  as constrained parameters.

  This limitation is most annoying.  Since to bypass it artificial types
  become necessary, making the situation more complex than necessary.

  For example the class ARRAY_COMPAR

    class ARRAY_COMPAR[T -> COMPARABLE]

    export
      repeat ARRAY,
      is_sorted, quick_sort, merge
  
    inherit
      ARRAY[T] rename Create as array_create
  
  can not be used with a basic type.  Trying to do so results in the
  error message

    Eiffel compilation manager
       (version 2.2 level A)
    Pass 3 on class test_array_compar
    "test_array_compar", 15: Basic type may not be used as actual generic type corresponding to
    a constrained formal generic parameter
    es: error in pass3

  I do not think the compiler should treat basic types any different from
  other types when being passed.  If the resulting code is more inefficent
  than so be it.  Otherwise a class is necessary for each basic type, as
  well as another class for non-basic types.  I do not believe this is
  in the "spirit" of Eiffels concepts on reusability.

Marc Pawlowsky   Graduate Student     Concordia University

bertrand@eiffel.UUCP (Bertrand Meyer) (02/20/90)

	In <1864@clyde.concordia.ca>, marcap@doyle.cs.concordia.ca (Marc Pawlowsky)
voices concerns about basic types and their use as parents in inheritance.

	I essentially agree with his observations. To reconstruct the sequence
of events:

	- In the original language design, as reflected in ``Object-Oriented
Software Construction'', four types (INTEGER, REAL, BOOLEAN, CHARACTER)
were treated specially, i.e. outside of the object-oriented context.
The reason was twofold: a concern for performance; and a feeling that
``everyone knows about integers and booleans anyway'' and that using the OO
framework to handle them was overkill.

	- After a while we recognized that it was in fact unpleasant to have
this ``magical'' status for basic types, and that (thanks to expanded types,
infix/prefix operators and constrained genericity, among others) it was indeed
possible to have a fully consistent type system, elegantly covering basic types as
well as user-defined types, without necessarily sacrificing implementation
performance.

	As a result, the basic types are treated as ``quasi-normal'' in the
release available since last summer (2.2). Internally, however, they are
still handled specially so as to guarantee a high level of efficiency
(3 + 4 is theoretically a routine call, but few Eiffel users would want it
to be implemented that way!). In fact, this part is one of the trickiest in
our implementation as we try to reconcile elegance of concept with efficiency
of implementation. Furthermore, there are still many carryovers from the
original implementation. For example (as pointed out by Norman Shelley
and others) having a function ``abs'' (absolute value) for integers
and a different one, ``rabs'', for real numbers, is anathema to the
object-oriented approach. I am not particularly proud of that one (which,
fortunately, will be easy to correct; it just escaped our attention).

	As we continue improving and generalizing the implementation, we
will strive to achieve the degree of generality desired by Mr. Pawlowsky
and other writers (and Eiffel users such as ourselves). Although we would
like INTEGER et al. to be conceptually treated exactly like any other
class-based expanded types, we cannot promise that ALL restrictions will
be lifted soon. We want Eiffel to remain implementable with a very small
performance overhead over something like C, and some tradeoffs need to be
made for this goal to remain realistic.
-- 
-- Bertrand Meyer
bertrand@eiffel.com

weiner@novavax.UUCP (Bob Weiner) (02/21/90)

> 	As a result, the basic types are treated as ``quasi-normal'' in the
> release available since last summer (2.2). Internally, however, they are
> still handled specially so as to guarantee a high level of efficiency
> (3 + 4 is theoretically a routine call, but few Eiffel users would want it
> to be implemented that way!). In fact, this part is one of the trickiest in

I do not agree with this statement since I believe that one cannot
inherit from the basic types in order to create a new class which
constrains the type's invariant further.  (I must admit that I have
tried to inherit from INT rather than INTEGER, and so forth, since there
is no class interface corresponding to INTEGER.)

Rather than sprinkle pre- and post-conditions around routines that say:

	require
	   param > 0

and other such things, it makes sense to create classes for POS_INT,
NON_NEG_INT and so forth and then type the formal parameters of the
routines.  The current implementation does not support this basic idea
and hence event the term 'quasi-normal' seems too strong to me.

I do hope that Eiffel V3.0 will correct this limitation.  Even better, I
would enjoy finding out that I am mistaken in my view.
-- 
Bob Weiner, Motorola, Inc.,   USENET:  ...!gatech!uflorida!novavax!weiner
(407) 364-2087

bertrand@eiffel.UUCP (Bertrand Meyer) (02/23/90)

From <1799@novavax.UUCP> by weiner@novavax.UUCP (Bob Weiner):

> Rather than sprinkle pre- and post-conditions around routines that say:
> 
>     require
>        param > 0
> 
> and other such things, it makes sense to create classes for POS_INT,
> NON_NEG_INT and so forth and then type the formal parameters of the
> routines.
> 
> I do hope that Eiffel V3.0 will correct this limitation.  Even better, I
> would enjoy finding out that I am mistaken in my view.

    I agree that it should be possible for programmers to define such
classes by inheriting from INT and adding the proper invariants.
This is an implementation problem and I have had good hope that we
(or others) will be able to solve it.

    Assuming this is the case, however, I am not so sure that
these classes should be added to the Kernel Library. This is because
I have some doubts about their practical applicability.
(This should be taken literally: at this point I am neither for or against
their inclusion in version 3, just doubtful.) The problem is that
POS_INT, NON_NEG_INT etc. are not *closed* for the arithmetic operations.
For example, with

    p1, p2: POS_INT;
    n1, n2: NEG_INT

there is no guarantee that p1 - p2 will be a POS_INT or that n1 - n2 will
be a NEG_INT. The best information we can deduce (the ``and'' in the
lattice of types) is just INT.

Similarly, if we define interval types of the form

    class INTERVAL export
        repeat INT, low, high
    inherit
        INT
    feature
        low, high: INTEGER;

        Create (a, b: INTEGER) is
                -- Set interval bounds to `a' and `b'.
            do
                low := a; high := b
            end; -- Create

    invariant
        value >= low; value <= high
    end -- class INTERVAL

they will not help us very much since for n of type INTERVAL we
have no static way of determining whether n + 1 is still
compatible.

    In all these cases we will have to resort to assertions anyway.

    All this is not surprising. We should not hope that a
static type system will magically solve problems that are
fundamentally dynamic in nature.

    The problem is not essentially different from the situation with
interval types in Pascal, as analyzed by A. Nico Habermann
(``Critical Comments on the Programming Language Pascal'',
Acta Informatica, 3, 1973, pages 47-57).

    The above is not a contention that Mr. Weiner is ``mistaken
in his view''. Simply, I am not convinced that the suggested classes
will solve more problems than they raise.

-- Bertrand Meyer
bertrand@eiffel.com

tynor@prism.gatech.EDU (Steve Tynor) (02/23/90)

In article <254@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes:
>From <1799@novavax.UUCP> by weiner@novavax.UUCP (Bob Weiner):
>
>> Rather than sprinkle pre- and post-conditions around routines that say:
>> 
>>     require
>>        param > 0
>> 
>> and other such things, it makes sense to create classes for POS_INT,
>> NON_NEG_INT and so forth and then type the formal parameters of the
>> routines.
...
>    In all these cases we will have to resort to assertions anyway.
...

It's really a case of where we have to assert. Surely much better in a class
than in each of its uses since the constraint is a property of the class.

Perhaps, for efficiency's sake, it would be acceptable to dissallow
multiple inheritance and feature (re)defintion for descendants of
basic types. Thus:

   class INTERVAL_1_10 export
      repeat INTEGER	
   inherit
      INTEGER
   invariant
      Current >= 1 and Current <= 10;
   end

Sure, this restricts us to static interval bounds, but then this is
all most programming languages provide. And it's better than what we
have now.

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
inherit STD_DISCLAIMER;
                     
    Steve Tynor
    Georgia Tech Research Institute
    Artificial Intelligence Branch
    tynor@prism.gatech.edu

rowley@bath.cs.ucla.edu (Michael T Rowley) (02/23/90)

In article <254@eiffel.UUCP>, bertrand@eiffel.UUCP (Bertrand Meyer) writes:
> [Intervals] will not help us very much since for n of type INTERVAL we
> have no static way of determining whether n + 1 is still
> compatible.
> 
>     In all these cases we will have to resort to assertions anyway.
> 
>     All this is not surprising. We should not hope that a
> static type system will magically solve problems that are
> fundamentally dynamic in nature.
> 
>     The problem is not essentially different from the situation with
> interval types in Pascal, as analyzed by A. Nico Habermann
> (``Critical Comments on the Programming Language Pascal'',
> Acta Informatica, 3, 1973, pages 47-57).
> 

I have to disagree with statement that these problems are dynamic in
nature.  Significant research has been done with "strengthened" types
to make it possible to catch range errors at compile time.

The main reference I have is Paul Eggert's 1981 Ph.D. dissertation,
which is available as a technical report from UCLA.  Since then, he
has gone to Twinsun, where they are developing "C-cured" which uses
strengthened types.

I am not an expert on the subject, but it involves having the compiler
know about subranges, and making sure that all assignments are to
conforming expressions (expressions that result in the same or smaller
range as the lvalue).  This changes the way some code is written, for
example "n = n + 1" is never allowed.  But the comparisons I have seen
between code written without vs. with strenthened types convinced me
that the restrictions do not add to many lines to the code.

The original version was from an Introductory programming textbook,
and the conversion brought to light 3 logic errors from the published
original.  Both the original and the converted version had 250-300
lines of code (depending on indenting style).

More recent papers could probably be obtained by contacting Paul
Eggert at Eggert@twinsun.com.

- MTR

P.S.  I have nothing to do with the C-cured project.  I saw a talk
given by him, and think that some of the concepts could benefit eiffel.