[comp.lang.eiffel] Eiffel cleanup #1: The creation mechanism

bertrand@eiffel.UUCP (Bertrand Meyer) (01/16/90)

A change planned for the object creation in Eiffel
--------------------------------------------------

In the tradition of glasnost' that has marked the evolution of Eiffel,
this posting and one or two subsequent ones will describe the changes
planned for version 3 of the language.

These are cleanup changes and do not affect anything fundamental.

----------------------------------------------------------------------
|WARNING: The change described here is planned for version 3 of the   |
|environment, not to be released until late 1990.                     |
                                                                      |
|Any change in the language supported by Interactive's tools          |
|will be accompanied by CONVERSION TOOLS to translate ``old'' syntax  |
|into new. Programmers will NOT need to perform any significant work  |
|to update existing Eiffel software.                                  |
|                                                                     |
|This posting is made solely for the purpose of informing the Eiffel  |
|community about ongoing developments. Although the posting has been  |
|preceded by careful reflection and internal discussions within       |
|Interactive, we make no commitment at this point that the features   |
|described here will actually be included, and, if they are, that     |
|their final form will be the exact one shown below.                  |
----------------------------------------------------------------------

Purpose of the change.
----------------------

The purpose of the change is to solve several problems at once through
an improved syntax and semantics for creating objects in Eiffel.
None of the problems is really major since, as indicated below, each
has a reasonable solution in the current language. Some of them are
small practical nuisances; others are irregularities which impair the
elegance of the overall language design.

Problem 1: In current Eiffel, there is only one creation procedure
    per class. To have more than one creation mechanism, you
    must use proper descendants (e.g. POLAR_POINT, CARTESIAN_POINT
    inheriting from POINT).

Problem 2: By default, the Create procedure is not inherited. To reuse
    the same creation mechanism in a proper descendant, you must rely
    on renaming.

Problem 3: There is no syntactical facility directly supporting the
    creation of an instance of a proper descendant. If a1 is of type
    A and you want to create it as an instance of B, a proper descendant
    of A, you must use an intermediate entity of type B.

Problem 4: Normal feature applications a.f (...) act on the object
    associated with reference a, and cannot change that reference
    itself. There are, however, three exceptions: Create, Forget and
    Clone. Such irregularity is unpleasant.

Problem 5: The ``Create'' procedure of a class has a somewhat
    special status. In particular, the current edition of ``Eiffel:
    The Language'' does not say clearly what happens if it is called
    unqualified by another procedure of the class.

Problem 6: There is an inconsistency between the status of Clone,
    a creation instruction (``Clone'' itself is a keyword)
    and the status of copy, deep_copy, deep_clone, all of
    which are routines of the universal class ANY. Conceptually,
    however, Clone does not appear fundamentally different.
    Furthermore, why the instruction is written x.Clone (y) rather
    than x := y.clone (with ``clone'' a function) is not
    clear; this has been pointed out by many people.


The language change
-------------------

    There is no more special role for a ``Create'' procedure.
Instead, a class may contain a clause

    creation
        f, g, h, ...

where the names listed are those of procedures of the class.
These are the ones that may be used for creating instances of the class.

``Create'' ceases to be a keyword; ``creation'' becomes one. (The risk
of clash with identifiers in existing classes appears minimal.)

Procedures appearing in the creation list are otherwise normal.
They may or may not appear in the export list. They may be introduced
in the class itself or be obtained from a proper ancestor.

A procedure which appears in the creation clause of a class may or
may not appear in the creation clause of its proper descendants.
As with the export clause, the creation clause may contain an item
of the form

    repeat A

where A is a proper ancestor; this is equivalent to a list of the creation
procedures of A.

There is only one creation instruction. Its most general form is

    x !D! f (...)

where x is a read-write entity of some type C and D is a descendant of
C; f must be a creation procedure of D. This creates an instance of
D, applies the default initializations, calls f with the actual arguments
given, and attaches the resulting object to x.

D may be omitted in the most frequent case, for which it is the same
as C, giving the form

    x !! f (...)

For a class which has no creation procedure (meaning that the default
initializations are sufficient to ensure the invariant), the syntax
becomes simply

    x !!

Because a creation procedure is a normal procedure, it may be used
as the target of a feature call, either local or (if the procedure
is exported) remote. For example, if a procedure

    set_polar (ro, theta: REAL)

is listed in both the ``export'' and ``create'' clauses of a class, then
for p of type POINT both of the following are correct, with a different
semantics:

    p !! set_polar (1, pi/2)  
            -- Create point with magnitude and angle given.

    p.set_polar (1, pi/2)
            -- Change point (previously created) to
            -- magnitude and angle given.

Cloning
-------

As suggested above, cloning ceases to be an instruction; a function

    clone: like Current

is now part of class ANY.

Forget will also disappear as an instruction. This is tied with
another small change (having to do with ``void'' values) and will be
described in another message.


A note on conversion of existing software
-----------------------------------------

    Conversion of existing Eiffel classes will be straightforward.
For a class with a Create procedure, it suffices to add a clause

creation
    create

Since ``create'' is no longer a keyword, nothing else changes.
Calls of the form a.Create (...) will be replaced by

    a!!create (...)
-- 
-- Bertrand Meyer
bertrand@eiffel.com

keithc@cognos.UUCP (Keith Campbell) (01/18/90)

In article <226@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes:
[about plans for version 3]

>There is only one creation instruction.  Its most general form is

>    x !D!f(...)

>where x is a read-write entity of some type C and D is a descendant of
>C; f must be a creation procedure of D.  This creates an instance of
>D, applies the default initializations, calls f with the actual arguments
>given, and attaches the resulting object to x.

>D may be omitted in the most frequent case, for which it is the same
>as C, giving the form

>    x !!f(...)

>For a class which has no creation procedure (meaning that the default
>initializations are sufficient to ensure the invariant), the syntax
>becomes simply

>    x !!

In the interest of further reducing useless local variables, I suggest
that you go one step further and allow creation of anonymous objects.
Thus

    x !D!f(...)
would be equivalent to
    x := !D!f(...)

The improvement is that now we can pass newly created objects as actual
parameters without the need for the declaration of local variables:

    foo( !D!f(...), !E!g(...), ... );

If the compiler is given the ability to infer the (most general) class
of object which needs to be created, then

    x := !!f(...)
would be equivalent to
    x !!f(...)

and

    x := !!
would be equivalent to
    x !!

All of us are comfortable with the use of the symbol "1" to mean

    !Integer!.incremented

Why not extend this flexibility to all classes?

Note that "incremented" cannot be confused with a creation feature
because it appears following the dot ".".  In this case, the compiler
must generate code to first create this object (using the default
initializations) before the "incremented" function may be applied.

-- 
Keith Campbell               Cognos Incorporated   S-mail: P.O. Box 9707
Voice: (613) 738-1338 x5222                                3755 Riverside Drive
  FAX: (613) 738-0002                                      Ottawa, Ontario
 UUCP: uunet!mitel!sce!cognos!keithc                       CANADA  K1G 3Z4

sommar@enea.se (Erland Sommarskog) (01/21/90)

Bertrand Meyer (bertrand@eiffel.UUCP) writes:
>There is only one creation instruction. Its most general form is
>
>    x !D! f (...)
>
>where x is a read-write entity of some type C and D is a descendant of
>C; f must be a creation procedure of D. This creates an instance of
>D, applies the default initializations, calls f with the actual arguments
>given, and attaches the resulting object to x.

While I have nothing against the concept as such, rather the
opposite, I am not appealed of the syntax. I have the opinion
that artificial symbols should be avoided, particulary if they
are not commonly used elsewhere. If you don't know Eiffel and
looks at a piece of code and sees "x !!" it will take some time
until you understand what is going on. (It is possible that !!
is common in this meaning. It is new to me, however.)

Maybe I have had a too big exposure to Cobol, but I would propose

   create x as D using f(...)

with "as D" and "using f(...)" being optional as in Dr. Meyer's
article. This would require a new keyword "using" and that "create"
would remain being one. Whether "using" is a common identifier, I
don't know, but I wouldn't expect it to be.
  This not only has the advantage to allievate the readability, but
also it gives a better understanding of what you are doing.

-- 
Erland Sommarskog - ENEA Data, Stockholm - sommar@enea.se
Unix is a virus.

bertrand@eiffel.UUCP (Bertrand Meyer) (01/21/90)

In <7862@cognos.UUCP>, keithc@cognos.UUCP (Keith Campbell) suggests
using creation expressions rather than creation instructions.
For example the instructions

[1] >     x := !D!f(...)
[2]       x := !D!

would assign to x the value of the right-hand side expressions, denoting
a newly created object of type D, (after initializing this object through
f in the case [1]). The advantage is that such an expression
can also be used in contexts other than assignments, for
example as actual argument to a routine.

In keeping with my earlier note, Mr. Campbell would also accept one of

[3] >     x := !!
[4]       x := !!f (...)

if the base class of x's type has no creation procedure.

Forms [3] and [4], however, do not seem acceptable. The right-hand side
is not a true expression since its meaning depends
on the type of x, which does not even occur in that right-hand
side. As Hoare pointed out in his ``Hints on Programming Language
Design'' Stanford CS Report, 1973, reprinted in Ellis Horowitz's
``Programming Languages: A Grand Tour'', Computer Science Press, 1983),
one of the pleasant properties of expressions is that an
expression such as E + F can be understood solely in terms
of its parts. Here this is clearly violated.

Mr. Campbell's major argument, when applied to this case, provides
further evidence of the same problem: obviously, forms [3] and [4]
cannot be used as anonymous actual arguments, since by themselves the
expressions are ambiguous (you cannot understand them without
further information on their intended type).

If we want to support anonymous creation expressions, then,
the only solution is to accept forms [1] and [2] only: in other words,
all creation operations must list the class.

This comes very close to the Simula 67 convention with a different syntax:
in Simula, object creation is obtained by writing expressions
(not instructions) of the form

    new class_name (arguments)

where the class_name is required. I considered this at the time of
the original Eiffel design but rejected it for several reasons,
including convenience.  Most creation operations (perhaps
90% in my experience) are of form [3] or [4] and it seems
to be a nuisance to require the programmer to repeat the
type of x in every case. 

It is interesting to relate this to another suggestion that I just
received through a private mail message. The author of that message
suggested that the initializing routine should be required;
in other words, forms [2] and [3] would not be supported.
This is easy to achieve: we could just have a procedure ``nothing''
(which does what its name indicates) in class ANY.
Then instead of an empty creation clause you would be required to write
a clause of the form

	creation
		nothing

if you want the class to have instances. But then if we combine this
suggestion with what the above discussion indicates as the result of
Mr. Campbell's suggestion, we have to write

[5]		x := !CLASS_NAME!nothing

instead of just

[6]		x !!

For creation instructions, a quite common occurrence in Eiffel software
(at least for basic software components, such as libraries),
I frankly prefer [6] to [5] and I think most programmers will agree.
-- 
-- Bertrand Meyer
bertrand@eiffel.com

chl@cs.man.ac.uk (Charles Lindsey) (01/23/90)

bertrand@eiffel.UUCP (Bertrand Meyer) writes:

>In keeping with my earlier note, Mr. Campbell would also accept one of

>[3] >     x := !!
>[4]       x := !!f (...)

>if the base class of x's type has no creation procedure.

>Forms [3] and [4], however, do not seem acceptable. The right-hand side
>is not a true expression since its meaning depends
>on the type of x, which does not even occur in that right-hand
>side. As Hoare pointed out in his ``Hints on Programming Language
>Design'' Stanford CS Report, 1973, reprinted in Ellis Horowitz's
>``Programming Languages: A Grand Tour'', Computer Science Press, 1983),
>one of the pleasant properties of expressions is that an
>expression such as E + F can be understood solely in terms
>of its parts. Here this is clearly violated.

Although Tony Hoare's principle enunciated above seems a fundamental part of
strong typing, there are precedents for breaking it in special very specific
circumstances (where the meaning of the exception is "obviously" OK). Thus in
PASCAL (also ALGOL 68) one may write

	somepointervariable := nil;

even though the type of nil can only be determined from its context. Also, in
ALGOL 68 one could write

	somevariable := SKIP; #SKIP returns an undefined value#

and even

	somevariable := GOTO idontcare; #to break off the computation entirely#

In the case if Eiffel, I should have thought that the same exception could be
made for x := !!. It is not so different from x := null which apparently
Bertrand is prepared to condone.

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

From <607@m1.cs.man.ac.uk> by chl@cs.man.ac.uk (Charles Lindsey):
> 
> Although Tony Hoare's principle [i.e., the meaning of an expression
> should be fully deducible from the meanings of its constituents]
> seems a fundamental part of strong typing, there are precedents for
> breaking it. [..] Thus in PASCAL (also ALGOL 68) one may write
> 
>     somepointervariable := nil;
> 
> even though the type of nil can only be determined from its context [...]
> 
> In the case if Eiffel, I should have thought that the same exception could be
> made for x := !!. It is not so different from x := null which apparently
> Bertrand is prepared to condone.

Actually it is different because in Pascal the type status of
`nil' is murky: nil is a constant which must belong to every type, which
is contradictory with the rest of the type system. No such problem arises
in Eiffel thanks to the introduction of NONE: because NONE is a descendant
of every class, we can define the type of `null' as being NONE, so that
the assignment

    x := null

is always typewise valid regardless of the type of x.



-- 
-- Bertrand Meyer
bertrand@eiffel.com