[net.lang.ada] Ada '88 musings

stt@ada-uts.UUCP (01/01/86)

Just to get a little meaty technical discussion going...
Here are some proposals for Ada '88, arising from
our use of Ada in building compilers, database managers, etc:

  1)  Support user-defined finalization for
      packages and limited types.
  2)  Support user-defined assignment for limited types.
  3)  Extend identifier visibility rules to eliminate need
      for most "use" clauses.

I consider (1) essential to using Ada for significant
systems programming, anytime multi-access or multi-tasking
exists in the program or the environment (to guarantee files
are closed, buffers are flushed, locks are released, storage is
reclaimed, etc.)

(2) is mostly a syntactic sugar issue, though any language
supporting the notion of abstract types ought to support it.

(3) is probably necessary for the upward-compatible
implementation of (2), to give correct visibility
characteristics to user-defined ":=".


One mechanism for implementing (1) would be as follows:

The "begin" of a declarative part would be extended
as follows:
   [  when end =>  STATEMENT_LIST  ] begin
The statement_list would be executed when the declarative part
is exited for any reason, after waiting for any tasks to complete.
Exceptions raised and not handled in the statement list would
cause the statement_list to be terminated, and the original exiting
action to continue.

In addition, a finalization procedure may be defined for
a limited type, with the following syntax:

    procedure "end"(OBJ: in out LIM_TYPE) is begin  ... end "end";
    
When such a procedure is visible (see discussion of (3) below), then
in addition to waiting for tasks to terminate, a block statement,
subprogram body, or task is not finished until each of the dependent
objects are finalized (by calling "end" on them).

Any exceptions raised and not handled by the "end" procedure
cause the procedure to return and processing continues with the
next "end" procedure.

For limited objects designated by access values, the "end"
procedure may be executed once no references remain, prior
to reclamation of the storage, but after termination of
all task components of the object.

Let me be the first to say that the proposed syntax is pretty
ugly, but user-defined finalization is so important that
in the absence of more creative suggestions, this would still
be worthwhile.

The implementation of (2) is pretty straightforward, stealing all
the rules from user-defined "=", except that the subprogram spec must be
of the form:
   procedure ":="(L : in out LIM_TYPE; R : in LIM_TYPE) is
   begin ... end ":=";

Restricting this to limited types is to eliminate confusion
with the many implicit assignments implied in legal
operations on non-limited types (e.g. aggregates, array assignment,
etc.).  These are the same as the arguments for user-defined "="
being restricted to limited types (with the same holes in the arguments,
presumably).

The problem with (2) is that built-in ":=" is "directly" visible
even when other implicitly defined operations (like "=") are not.
To avoid forcing everybody to "use" the defining package
of a limited type to get visibility on ":=" (or the horrendous
pkg.":="(a, b)), we come to:

The implementation for (3):
Add a third place to search for identifiers after consulting
"direct" visibility and "use" visibility -- "defining-package"
visibility.  Defining-package visibility would mean that
after consulting the "use"d packages, each package in which
the type of a parameter is defined is consulted for a subprogram/operator
definition.  Note that the package of the result-type is NOT included.

In fact, if more than one package is represented among the types
of the parameters, then it is guaranteed that at most one
of them could have visibility to all of the parameter types,
and hence only one of them could possibly provide a definition
for the subprogram/operator.

For example:
    x+y    Assume type of x is pkg_x.type_x and type of y is pkg_y.type_y
           and pkg_y looks like this:
	   
           with pkg_x;
	   package pkg_y is
	       type type_y is new integer;
	       function "+"(a:pkg_x.type_x; b:type_y) return type_y;
           end pkg_y

   Then it should not be necessary to "use" pkg_y for Ada semantics
to resolve "+" in x+y.
This should eliminate most of the need for "use" clauses.

All of these ideas are half-baked, but we still have 2 years
to finish the baking...

Tucker Taft
Intermetrics, Inc.
733 Concord Ave
Cambridge, MA  02138   (617) 661-1840

creedy@cca.UUCP (Christopher Reedy) (01/02/86)

In article <> stt@ada-uts.UUCP writes:

>Here are some proposals for Ada '88, arising from
>our use of Ada in building compilers, database managers, etc:
>
>  1)  Support user-defined finalization for
>      packages and limited types.
>  2)  Support user-defined assignment for limited types.
>  3)  Extend identifier visibility rules to eliminate need
>      for most "use" clauses.
 . . .
>One mechanism for implementing (1) would be as follows:
 . . .
>
>Let me be the first to say that the proposed syntax is pretty
>ugly, . . .
(Let me be the first to agree with you :-)  Actually, I like this idea.
However, I think you left out support for user defined initialization
for limited types.  Without this it does not seem possible to determine
when an object is uninitialized except for the case of access variables, 
where the compiler supplies a default initialization.  (See comment to #2
below.)
>
>The implementation of (2) is pretty straightforward, stealing all
>the rules from user-defined "=", except that the subprogram spec must be
>of the form:
>   procedure ":="(L : in out LIM_TYPE; R : in LIM_TYPE) is
                       ^^^^^^  
>   begin ... end ":=";
Interesting choice.  I would have thought that the proper form of the
argument is "out", not "in out".  The semantics of "in out" would seem to 
require that the object be initialized prior to invoking the procedure. 
This isn't possible if the assignment is specifically intended to 
initialize the object.  This usage might be feasible if there is a defined
initialization (compiler or user) for objects of type LIM_TYPE.
>
>The implementation for (3):
>Add a third place to search for identifiers after consulting
>"direct" visibility and "use" visibility -- "defining-package"
>visibility.  . . .
I also like this idea.  However, in keeping with the general philosophy 
of Ada that "hidden" defaults should not be a source of confusion to the
programmer and maintainer, it would seem that something like a limited
form of USE that makes the operator symbols visible without making the
entire package visible would be more appropriate.

Chris Reedy

stt@ada-uts.UUCP (01/03/86)

Initialization may be specified for limited types which are
record types, by supplying default expressions for the components.
By nesting any type within a record (possibly discriminated), it
is possible to specify a relatively arbitrary initialization.

For example:
type lim(x : integer) is limited private;
 ...
type lim(x : integer) is
    record
        contents : exciting_type(x) := complicated_function(x);
    end record;

bmac3@ssc-bee.UUCP (Scott Pilet) (01/09/86)

>   3)  Extend identifier visibility rules to eliminate need
>       for most "use" clauses.
> 
> (3) is probably necessary for the upward-compatible
> implementation of (2), to give correct visibility
> characteristics to user-defined ":=".
> 
> The problem with (2) is that built-in ":=" is "directly" visible
> even when other implicitly defined operations (like "=") are not.
> To avoid forcing everybody to "use" the defining package
> of a limited type to get visibility on ":=" (or the horrendous
> pkg.":="(a, b)), we come to:
> 
> The implementation for (3):
> Add a third place to search for identifiers after consulting
> "direct" visibility and "use" visibility -- "defining-package"
> visibility.  Defining-package visibility would mean that
> after consulting the "use"d packages, each package in which
> the type of a parameter is defined is consulted for a subprogram/operator
> definition.  Note that the package of the result-type is NOT included.
> 
> In fact, if more than one package is represented among the types
> of the parameters, then it is guaranteed that at most one
> of them could have visibility to all of the parameter types,
> and hence only one of them could possibly provide a definition
> for the subprogram/operator.
> 
> For example:
>     x+y    Assume type of x is pkg_x.type_x and type of y is pkg_y.type_y
>            and pkg_y looks like this:
> 	   
>            with pkg_x;
> 	   package pkg_y is
> 	       type type_y is new integer;
> 	       function "+"(a:pkg_x.type_x; b:type_y) return type_y;
>            end pkg_y
> 
>    Then it should not be necessary to "use" pkg_y for Ada semantics
> to resolve "+" in x+y.
> This should eliminate most of the need for "use" clauses.
> 
> All of these ideas are half-baked, but we still have 2 years
> to finish the baking...
> 
> Tucker Taft
> Intermetrics, Inc.
> 733 Concord Ave
> Cambridge, MA  02138   (617) 661-1840

In a large system effort, some other programmer may have a package
with a y and a "+" that would allow x+y.  The lack of a "use" statement
adds to the complexity for debugging and sets the system up for a disaster
in the future.  The existence of a "use" statement may be nice for small
Ada programs, but in a large project the use of it may add to the confusion.
"with"ing and "rename"ing ensures that the software engineer is positive
of where an identifier is from and what it is.  More errors will be detected
by the software tools used by the engineers in a large software project
following this type of methodology.  As a tool to aid the software engineer,
a compiler message suggesting some resolution of an undefined identifier,
operator, or procedure (etc.) is helpful, but a compiler allowing ambiguities
that are technologically preventable can be disaster.

scott pilet
--these statements are my own and not Boeing's