Version 1.1 of ais/ai-20217.txt

Unformatted version of ais/ai-20217.txt version 1.1
Other versions for file ais/ai-20217.txt

!standard 07.01 (02)          01-09-21 AI95-00277/00
!class amendment 01-09-21
!status work item 01-09-21
!status received 01-09-21
!priority Medium
!difficulty Hard
!subject Handling mutually recursive types via forever incomplete types
!summary
!problem
Ada only allows mutually recursive types to be declared if they are all declared within the same library unit. This can force all of the major data structures of a program into a single library unit, which is clearly undesirable in some situations.
The goal of the proposed features is to allow mutual recursion among separately compiled types (types containing (pointers to) each other as components, and primitive operations with (pointers to) each other as parameters.) And to do this in a way that doesn't place any other restrictions on the programmer.
!proposal
!discussion
(*** TBD ***)
The concept of a package abstract has been discussed before, under different guises. At the time, the "with type" seemed somewhat more attractive. However, various technical difficulties associated with making access types visible via "with type" have reduced the capability of the "with type" proposal. Also, implementation discussions about how to implement "with type" have revealed some significant issues associated with whether or not the specification must be in the environment prior to compiling a "with type" clause. Other problems that have arisen relate to the need to implicitly create an "abstract" for a package to implement "with type," with the attendant issues associated with implicit structures in the program library, their longevity, their consistency, etc. Finally, "with type" requires creating some rather strange "fictions" to explain to the user what is really going on.
There seem to be two general ways this kind of problem is solved in other languages. One approach is to permit unrestricted forward references. This is the approach adopted by Eiffel, Java, and Smalltalk. The other approach is to require "forward" or "incomplete" declarations, sometimes called "signatures" or "partial revelations." This is generally the approach adopted by Ada, as well as Pascal, Modula, ML, C/C++, etc. The notion of a package "abstract" is analogous to the "incomplete" type in Ada, the signature in ML, and the partial revelation in Modula.
The "with type" approach seems to be a bit of an awkward half-way position, allowing a kind of "restricted" forward reference, lacking the generality of unrestricted forward reference, and lacking the flexibility of a true "forward" declaration, where representation items and other capabilities can be accommodated.
The package abstract approach seems more natural for Ada, and gives the user something to look at and conceptually "hold onto," which should make the concept easier to understand.
From an implementation point of view, the "optionality" of a package abstract is analogous to the "optionality" of a subprogram specification, and presumably similar program library mechanisms can be used to accommodate them.
Currently, a package has three parts: visible part, private part, and body. The visible and private parts are syntactically combined, because the compiler needs to see them both at once, in order to generate reasonably efficient code. More precisely, when compiling a compilation_unit that says "with P;" the compiler needs to look at both the visible and private parts of P.
This proposal adds another part, so we have four: abstract, visible part, private part, and body. In terms of syntax, these form three compilation units: abstract, spec, and body. The visibility rules work as usual, considering an abstract to come before the visible part/private part/body.
!example
Here is the classic case of mutual dependence, where an employee belongs to a particular department, and a department has a manager who is an employee.
package abstract Employees is
type Employee; type Emp_Ptr is access all Employee;
end Employees;
package abstract Departments is type Department; type Dept_Ptr is access all Department; end Departments;
with Departments abstract; package Employees is type Employee is private; type Emp_Ptr is access all Employee; procedure Assign_Employee(E : access Employee; D : access Departments.Department); ... function Current_Department(D : access constant Employee) return Departments.Dept_Ptr; end Employees;
with Employees abstract; package Departments is type Department is private; type Dept_Ptr is access all Department; procedure Choose_Manager(D : access Department; Manager : access Employees.Employee); ... end Departments;
!ACATS test
!appendix

From: Dan Eilers
Sent: Wednesday, June 20, 2001, 5:04 PM

I agree with Tucker et al that the "with type" proposal (AI 217) is
misguided, because it involves implicit type declarations and their
enclosing "ghost" library units.  These implicit declarations
provide no place to attach pragmas, representation clauses or
access type declarations.  They also don't work well in library-based
implementations, because such implementations can't easily go looking
for the source to an uncompiled unit to see what types are declared
inside it.  Instead, library-based implementations would probably need
some new compilation mode that enters the ghost library packages into
the library.

While the "package abstracts" proposal (AI 271) solves these problems,
I agree with Ed Schonberg et al that this solution is too heavy.  It
increases the number of package parts to 4 (abstract, visible spec,
private, body); it adds a new kind of library unit, causing impact to
tools that manipulate libraries, e.g. compilation-order tools; and
it adds a new kind of "with" clause, e.g. abstract with p, or
with p abstract, or with p'abstract.

I agree with Bob Duff et al that the alternative of bolting the package
abstract to the front of the package spec isn't attractive.  It doesn't
reduce the 4 kinds of package parts; it doesn't eliminate the need
for some new compilation mode that enters just the package abstract into
the library (although such a new compilation mode might be easier to
define and implement); and it doesn't eliminate the need for the
new kind of "with" clause.

The essence of the "package abstracts" proposal is that it provides
a place where an incomplete type can be declared whose full declaration
is expected to be found elsewhere.  (The name of the package abstract
provides the indication as to where the full type declaration is expected
to be found.)  But there seems to be no need for a new type of library
unit to do this job.  Instead, a pragma (or spiffy new syntax for
incomplete type declarations if you prefer) would suffice.

Instead of:

   package abstract Employees is
        type Employee;
        type Emp_Ptr is access all Employee;
    end Employees;

You would have:

   package unrelated is
       type Employee;
       pragma completed_elsewhere(Employee, where => Employees);
       type Emp_Ptr is access all Employee;
   end unrelated;

This involves no new package parts, no new library units, and no
new kinds of "with" clauses.  If packages Employees and Departments
were both children of the same parent, then the incomplete
declarations could simply go in the parent.

        -       -       -       -       -       -       -

As a further refinement, I would note that the "where" parameter
to pragma completed_elsewhere is anomalous, since it names a
package that isn't visible, and normally isn't even available in
the library.  I don't think the "where" parameter is actually
necessary.  Instead of the incomplete type declaration indicating
where the full type will be (so that the compiler can ultimately
connect the two), the full declaration could indicate what
incomplete type it is completing.  This could be done with a
pragma (or spiffy new syntax if you prefer).

So instead of:

    package abstract Employees is
        type Employee;
        type Emp_Ptr is access all Employee;
    end Employees;

    package abstract Departments is
        type Department;
        type Dept_Ptr is access all Department;
    end Departments;

    with Departments'abstract;
    package Employees is
        type Employee is private;
        type Emp_Ptr is access all Employee;
        procedure Assign_Employee(E : access Employee;
          D : access Departments.Department);
        ...
        function Current_Department(D : access constant Employee) return
          Departments.Dept_Ptr;
    end Employees;

    with Employees'abstract;
    package Departments is
        type Department is private;
        type Dept_Ptr is access all Department;
        procedure Choose_Manager(D : access Department;
          Manager : access Employees.Employee);
        ...
    end Departments;

we would have:

   package unrelated is
       type Employee;  pragma completed_elsewhere(Employee);
       type Emp_Ptr is access all Employee;

       type Department;  pragma completed_elsewhere(Employee);
       type Dept_Ptr is access all Department;
   end unrelated;


    with Unrelated;
    package Employees is
        type Employee is private;
           pragma completion(Employee, from => Unrelated);
        type Emp_Ptr is access all Employee;
        procedure Assign_Employee(E : access Employee;
          D : access Unrelated.Department);
        ...
        function Current_Department(D : access constant Employee) return
          Unrelated.Dept_Ptr;
    end Employees;

    with Unrelated;
    package Departments is
        type Department is private;
           pragma completion(Department, from => Unrelated);
        type Dept_Ptr is access all Department;
        procedure Choose_Manager(D : access Department;
          Manager : access Unrelated.Employee);
        ...
    end Departments;


This opens up the possibility of more than one completion of the same
incomplete type, which may be unnecessary, but doesn't seem harmful.

To summarize:
   no new kinds of library units
   no new kinds of package parts
   no new kinds of with clauses
   no impact to compilation-order tools
   no new compilation modes
   no forward references to uncompiled library units
   no problems with library-based implementations
   no new syntax unless we want it (we probably do).

*************************************************************

From: Randy Brukardt
Sent: Tuesday, July 3, 2001, 6:14 PM

Re: Dan Eiler's proposal.

I'm quite surprised that no one seriously has looked at this proposal. It seems
to me that it eliminates some of the problems of the abstract package and
simplifies it a lot without necessarily losing the important features.

The basic proposal is to introduce a new kind of incomplete type that NEVER
needs to be completed. It would have syntax like (Dan used pragmas, but they're
really ugly for something that will be commonly used):

    type <Defining_Identifier> is separate;
    type <Defining_Identifier> is tagged separate;

The rules for packages would be unchanged. The tagged kind of separate
incomplete type would allow 'Class and use as the type name in parameter
declarations (but not function returns).

My immediate reaction was that this would be hard to implement. But, on further
reflection, I realized that as long as the incomplete rules were enforced
everywhere that the incomplete type is visible, the compiler NEVER needs to
know the real type involved. Moreover, this type could not be used in an
allocator or a generic, so we would get the restrictions we need for free.
(And it is rather like an incomplete type deferred to a body, for which the
compiler can't assume anything anyway.)

The completion would include a new clause in the type declaration:

    type <Defining_Identifier> [completion_clause] ...

    completion_clause ::= for <type_name>

[I'd rather use "completes" here, but that would introduce a new reserved word.
I also looked at "declare" for this use, but it reads funny. I'm sure someone
will have nifty syntax for this.]

There would be a post-compilation check that all "is separate" types are
completed exactly once. (I don't think that there is any benefit to allowing
multiple completions, because the types have to be considered the same in some
scopes, and that could lead to a situation where A = P and B = P, but A /= B.)

To recast Dan's example (fixing a couple of errors):

    package Partial_Definitions is
       type Employee is separate;
       type Emp_Ptr is access all Employee;

       type Department is separate;
       type Dept_Ptr is access all Department;
   end Partial_Definitions;

   with Partial_Definitions;
   package Employees is
       type Employee for Partial_Definitions.Employee is private;
       type Emp_Ptr is access all Employee;
       procedure Assign_Employee(E : access Employee;
          D : access Partial_Definitions.Department);
        ...
       function Current_Department(D : access constant
          Employee) return Partial_Definitions.Dept_Ptr;
   end Employees;

   with Partial_Definitions;
   package Departments is
       type Department for Partial_Definitions.Department is private;
       type Dept_Ptr is access all Department;
       procedure Choose_Manager(D : access Department;
          Manager : access Partial_Definitions.Employee);
        ...
   end Departments;


Now to look at the possible problems:

Determining the representation of Partial_Definitions.Emp_Ptr would be a
problem for some implementations that select the representation of an access
type based on the designated type. However, this would be no different than
doing that for a type whose completion is deferred to the body. So such
implementations must already be able to handle this.

Similarly, Partial_Definitions.Emp_Ptr would never be frozen, but again we
already have to handle that.

Partial_Definitions.Emp_Ptr would never have allocators defined for it.
Probably it would be best that it has no pool at all. That means that any
allocations would have to be converted to the type. Similarly,
Unchecked_Deallocation couldn't be instantiated for it. OTOH, other uses would
not need conversions. So that doesn't seem too bad. If it is a real problem,
AI-230 may help solve that. Or we could simply define such types to be
equivalent (that couldn't be a compatibility problem, as Ada 95 has no such
types!). But then we'd have to deal with defining the pool for the incomplete
access type.

The interesting question is exactly what does the "completion_clause" mean.
The idea appears to be that inside of package Employees,
Partial_Definitions.Employee and Employees.Employee are considered to be the
same type for all purposes including resolution. (That's how we get conversions
between Partial_Definitions.Emp_Ptr and Employees.Emp_Ptr.) Whether that should
hold outside of Employees at places that have visibility on both Employees and
on Partial_Definitions is an open question. Off-hand, I don't see how this
could cause a problem, but perhaps someone else can.


So, at first look, this seems like a useful idea to pursue. It doesn't have any
elaboration or "new unit kind" problems as package abstract does, and it
probably doesn't have the visibility issues either (depending on the exact
definition of when the types are considered the same). It's similar to the
current "with type" in that the access type equivalence isn't perfect, but it
looks much easier to fix than rather than with "with type".

So, what is wrong with this idea? Should it be written up completely?

*************************************************************

From: Robert Dewar
Sent: Tuesday, July 3, 2001, 7:55 PM

I certainly prefer the separate idea to the package abstract proposal from
a language design point of view, but it is not clear to me that it satisfies
all the Java interface requirements. I definitely think it is worth writing
up the idea completely.

*************************************************************

From: Randy Brukardt
Sent: Tuesday, July 3, 2001, 8:19 PM

Humm. The only Java interface requirement that I know of that we're trying to
address with this proposal is the mutually dependent types problem. And Java
types are essentially tagged types in Ada, so with the tagged incomplete type,
we get the needed functionality. I think. Is there something else that I've
missed?

*************************************************************

From: Ted Baker
Sent: Thursday, July 5, 2001, 7:05 AM

I've been lurking out here watching the discussion on the package abstract
idea, dismayed by the conceptual complexity and ugliness.  The Dan/Randy
separate proposal seems much cleaner.  I hope you can work it out to everyone's
satisfaction.

*************************************************************

From: Tucker Taft
Sent: Monday, July 9, 2001, 3:51 PM

Ted Baker wrote:
>
> I've been lurking out here watching the discussion on the package abstract
> idea, dismayed by the conceptual complexity and ugliness.  The Dan/Randy
> separate proposal seems much cleaner.  I hope you can work it out to everyone's
> satisfaction.  --Ted

I agree that Dan's proposal, with some of Randy's suggestions,
is a direction worth pursuing.  This is not too far from
a direction suggested in the past using pragma Import
and pragma Export applied to types, analogous to the
trick that can be used to implement a subprogram declared
in one package with a subprogram declared in another package.

I would like to know at least the name of the
package where the full type definition is provided,
and I wouldn't mind requiring it to be a child
of the package where the incomplete type is declared,
ensuring that the incomplete/separate type's declaration
is directly visible at the point of the full type definition.
I would require that the name of the full type in the child
match the name of the incomplete type in its parent.

Hence, something like:

   package P is
       type T is separate(C);  -- is completed
                               -- in child "C" of P.
       ...
   end P;

   package P.C is
       type T is ...
   end P.C;

*************************************************************

From: Randy Brukardt
Sent: Friday, July 13, 2001, 6:15 PM

> I agree that Dan's proposal, with some of Randy's suggestions,
> is a direction worth pursuing.  This is not too far from
> a direction suggested in the past using pragma Import
> and pragma Export applied to types, analogous to the
> trick that can be used to implement a subprogram declared
> in one package with a subprogram declared in another package.

Except this is a model that makes sense, while using pragma Import and Export
says KLUDGE in large, flashing letters.

> I would like to know at least the name of the
> package where the full type definition is provided,
> and I wouldn't mind requiring it to be a child
> of the package where the incomplete type is declared,
> ensuring that the incomplete/separate type's declaration
> is directly visible at the point of the full type definition.
> I would require that the name of the full type in the child
> match the name of the incomplete type in its parent.
>
> Hence, something like:
>
>    package P is
>        type T is separate(C);  -- is completed
>                                -- in child "C" of P.
>        ...
>    end P;
>
>    package P.C is
>        type T is ...
>    end P.C;

Well, this is almost OK, but I think it has some problems.

First, describing the semantics of the name in the separate is going to be
interesting. It has to reference something not declared yet, and about all we
can say about it is that it is a library unit -- which doesn't even need to
exist yet. Indeed, there must be no requirement that it exist.

Second, restricting the completion to a child is a non-starter. I certainly
don't think people would be happy to have to rename all of their packages in
order to use this feature. It is particularly bad, because the parent
(presumably with the original name) would contain just the partial definitions,
while the child would contain the "meat" -- the stuff that exists now. So every
reference the package would have to be changed.

Moreover, I don't think such a restriction would work well with Robert Dewar's
"only reason for this feature" -- interfacing to Java. If you have two Java
classes Class_A and Class_B that are mutually recursive, this idea would force
you to introduce not only another package, but one whose name will show up
everywhere:

    package Java_Stuff is
       type Class_A is tagged separate (A);
       type Class_A_Access is access all Class_A;
       type Class_B is tagged separate (B);
       type Class_B_Access is access all Class_B;
    end Java_Stuff;

    package Java_Stuff.A is
       type Class_A is tagged ...
       ...
    end Java_Stuff.A;

    package Java_Stuff.B is
       type Class_B is tagged ...
       ...
    end Java_Stuff.B;

I think it would be much cleaner to say:

    package Abstract_A_and_B is
       type Class_A is tagged separate (A);
       type Class_A_Access is access all Class_A;
       type Class_B is tagged separate (B);
       type Class_B_Access is access all Class_B;
    end Abstract_A_and_B;

    with Abstract_A_and_B;
    package A is
       type Class_A is tagged ...
       ...
    end A;

    with Abstract_A_and_B;
    package B is
       type Class_B is tagged ...
       ...
    end B;

...because then the "abstract" package never appears in uses of Class_A or
Class_B. Only the declarations of Class_A and Class_B need worry about it.

It is especially annoying that there is no technical reason (at least that I
know) for this restriction: it would just be a language designer's feel for
"the best way to do it". There doesn't seem to be any technical need for direct
(or any!) visibility on the incomplete type in order to complete it - the type
has no operations (primitive or otherwise) anyway. And the visibility of uses
doesn't seem to matter either: you just get whatever view you have.

So I would make the name a library unit name, and drop any child requirement.

A third point (just a quibble) is that it really is necessary for readability
to mention that this type is a completion (even if the name of the type is
required to be the same). So, I'd expect to always put in a comment:
    -- Completes Abstract_A_and_B.Class_B.
It seems like it would be helpful to include this in the syntax somehow, just
as is done with subprogram names on "end". (Aside: I sure wish "end record;"
was "end <type_name>;", 'cause that's what I write every time anyway. :-)

Anyway, it seems that I've volunteered again :-) to write this up. I'm not sure
whether to replace AI-271 with this proposal, or to open a third AI for this
third approach. Suggestions?

*************************************************************

From: Tucker Taft
Sent: Monday, July 16, 2001, 5:31 PM

Randy Brukardt wrote:
> >
> >    package P is
> >        type T is separate(C);  -- is completed
> >                                -- in child "C" of P.
> >        ...
> >    end P;
> >
> >    package P.C is
> >        type T is ...
> >    end P.C;
>
> Well, this is almost OK, but I think it has some problems.
>
> First, describing the semantics of the name in the separate is going to be
> interesting. It has to reference something not declared yet, and about all
> we can say about it is that it is a library unit -- which doesn't even need
> to exist yet. Indeed, there must be no requirement that it exist.

This is analagous to a subunit.  The subunit need not
exist at the point when the stub appears, but it logically
appears there.  So this seems pretty similar.

>
> Second, restricting the completion to a child is a non-starter. I certainly
> don't think people would be happy to have to rename all of their packages in
> order to use this feature. It is particularly bad, because the parent
> (presumably with the original name) would contain just the partial
> definitions, while the child would contain the "meat" -- the stuff that
> exists now. So every reference the package would have to be changed.

Hmmm... I suppose, though this seems like something
that would have to be mostly for new code.  Also,
library-unit renaming would allow the child to be
renamed as a top-level unit.

>
> Moreover, I don't think such a restriction would work well with Robert
> Dewar's "only reason for this feature" -- interfacing to Java. If you have
> two Java classes Class_A and Class_B that are mutually recursive, this idea
> would force you to introduce not only another package, but one whose name
> will show up everywhere:
>
>     package Java_Stuff is
>        type Class_A is tagged separate (A);
>        type Class_A_Access is access all Class_A;
>        type Class_B is tagged separate (B);
>        type Class_B_Access is access all Class_B;
>     end Java_Stuff;
>
>     package Java_Stuff.A is
>        type Class_A is tagged ...
>        ...
>     end Java_Stuff.A;
>
>     package Java_Stuff.B is
>        type Class_B is tagged ...
>        ...
>     end Java_Stuff.B;

Actually, Java classes are essentially always declared
in (Java) packages, so the Ada package corresponding
to the Java package is a natural place for these.
Also, the pointer type is the one that Java users will
use.  The package where the operations are declared
will not be as important if the obj.operation notation
is used.

>
> I think it would be much cleaner to say:
>
>     package Abstract_A_and_B is
>        type Class_A is tagged separate (A);
>        type Class_A_Access is access all Class_A;
>        type Class_B is tagged separate (B);
>        type Class_B_Access is access all Class_B;
>     end Abstract_A_and_B;
>
>     with Abstract_A_and_B;
>     package A is
>        type Class_A is tagged ...
>        ...
>     end A;
>
>     with Abstract_A_and_B;
>     package B is
>        type Class_B is tagged ...
>        ...
>     end B;
>
> ...because then the "abstract" package never appears in uses of Class_A or
> Class_B. Only the declarations of Class_A and Class_B need worry about it.

I guess I am still not convinced.  I think we need some
real examples, not just package A and B, to see which
works better.

> It is especially annoying that there is no technical reason (at least that I
> know) for this restriction: it would just be a language designer's feel for
> "the best way to do it". There doesn't seem to be any technical need for
> direct (or any!) visibility on the incomplete type in order to complete it -
> the type has no operations (primitive or otherwise) anyway. And the
> visibility of uses doesn't seem to matter either: you just get whatever view
> you have.
>
> So I would make the name a library unit name, and drop any child
> requirement.

But it seems important to know that a given type is
completing an incomplete type, because representation
may depend on this.  I would think we would certainly
require that that package with the full type "with" the
package with the incomplete type, so given that, it seemed
better to just make it into a child, which always implicitly
"with"s its parent.

>
> A third point (just a quibble) is that it really is necessary for
> readability to mention that this type is a completion (even if the name of
> the type is required to be the same). So, I'd expect to always put in a
> comment:
>     -- Completes Abstract_A_and_B.Class_B.
> It seems like it would be helpful to include this in the syntax somehow,
> just as is done with subprogram names on "end". (Aside: I sure wish "end
> record;" was "end <type_name>;", 'cause that's what I write every time
> anyway. :-)
>
> Anyway, it seems that I've volunteered again :-) to write this up. I'm not
> sure whether to replace AI-271 with this proposal, or to open a third AI for
> this third approach. Suggestions?

I hate to see a proliferation of AIs which are alternatives
of the same thing.  I guess I would rather see these all
part of the same AI, put forth as alternatives.  Once
we ultimately pick one for further refinement, we will want
the others in the appendix or rationale as rejected alternatives.
Hence, it seems to me they all belong in the same AI.
Perhaps we need to recognize the importance of "alternatives"
in amendment AIs.

*************************************************************

From: Randy Brukardt
Sent: Tuesday, July 17, 2001, 4:41 PM

Tucker, responding to me responding to him, wrote:
> > >
> > >    package P is
> > >        type T is separate(C);  -- is completed
> > >                                -- in child "C" of P.
> > >        ...
> > >    end P;
> > >
> > >    package P.C is
> > >        type T is ...
> > >    end P.C;
> >
> > Well, this is almost OK, but I think it has some problems.
> >
> > First, describing the semantics of the name in the separate is going to be
> > interesting. It has to reference something not declared yet, and about all
> > we can say about it is that it is a library unit -- which doesn't even need
> > to exist yet. Indeed, there must be no requirement that it exist.
>
> This is analagous to a subunit.  The subunit need not
> exist at the point when the stub appears, but it logically
> appears there.  So this seems pretty similar.

I think you missed the point. In a subunit, there is no attempt to name some
unit that will exist sometime in the future. The stub is a declaration of a
some entity, and that entity actually exists there.

OTOH, the unit named in your proposed syntax is not declared anywhere. And the
appearance in the separate type declaration certainly is not a declaration, nor
does some random package spec logically exist at this point.

Traditionally, Ada 95 requires the completor to name the completee (think
"separate" clause in subunits). The partial declaration does not indicate much
(if anything) about the completor.

Thus, I think it makes the most sense for the completion to indicate that it is
completing a separate, rather than the separate trying to name the location of
the completion. (But I don't feel that strongly about this.)

> > Second, restricting the completion to a child is a non-starter. I certainly
> > don't think people would be happy to have to rename all of their packages in
> > order to use this feature. It is particularly bad, because the parent
> > (presumably with the original name) would contain just the partial
> > definitions, while the child would contain the "meat" -- the stuff that
> > exists now. So every reference the package would have to be changed.
>
> Hmmm... I suppose, though this seems like something
> that would have to be mostly for new code.  Also,
> library-unit renaming would allow the child to be
> renamed as a top-level unit.

Well, I suppose. *I* have existing code that desperately needs this feature,
and I would certainly want to use it when it is available.

But this means that virtually my entire program would have to be children of
some parent. The effect would be that every object would have a parent package
containing the abstract interface; and a child containing the real definition,
and a library unit renames to a reasonable name.

>...
>
> Actually, Java classes are essentially always declared
> in (Java) packages, so the Ada package corresponding
> to the Java package is a natural place for these.
> Also, the pointer type is the one that Java users will
> use.  The package where the operations are declared
> will not be as important if the obj.operation notation
> is used.

I know all of this (with the possible exception of the last), and I don't see
what it has to do with my point. Every Java object is converted into an Ada
package. But if incomplete types are restricted to children, each such package
will necessarily be two such packages. And the real implementation will be in
a child. So, the package names will not be a direct mapping from Java, and
you'll need a library renames to get close.

> > It is especially annoying that there is no technical reason (at least that
> > I know) for this restriction: it would just be a language designer's feel
> > for "the best way to do it". There doesn't seem to be any technical need
> > for direct (or any!) visibility on the incomplete type in order to
> > complete it - the type has no operations (primitive or otherwise) anyway.
> > And the visibility of uses doesn't seem to matter either: you just get
> > whatever view you have.
> >
> > So I would make the name a library unit name, and drop any child
> > requirement.
>
> But it seems important to know that a given type is
> completing an incomplete type, because representation
> may depend on this.  I would think we would certainly
> require that that package with the full type "with" the
> package with the incomplete type, so given that, it seemed
> better to just make it into a child, which always implicitly
> "with"s its parent.

If your initial statement is true (and it seems to be), then it is imperative
that the completing declaration include syntax to specify that it is a
completion. (So all of the reader, the writer, and the compiler knows certainly
that it is a completion, and of what.) That syntax would necessarily include
the name of the type being completed, so it follows that the package would
have to be withed (from the normal visibility rules). Once we have this, there
doesn't seem to be any need to restrict where this is used.

*************************************************************

From: Pascal Leroy
Sent: Tuesday, July 17, 2001, 7:11 AM

Robert says:

> The main argument in favor of this feature at all (at least the market
> argument as opposed to "this would be nice to have") is to mimic C++ and
> Java existing code. So to me the only strong argument for nested package
> abstracts would be an argument that they are useful from this point of view.
> I do not see that this is the case, but am not really familiar enough to say.

I have an entirely different perspective here.  I don't think Rational will
implement interfacing with C++ or Java in the foreseeable future (I don't think
we have seen much demand).  On the other hand, mutually dependent types show up
all the time in OO designs (in particular when mapping a UML model to Ada).  So
I believe we need a general solution to this issue, regardless of interfacing
consideration (general doesn't mean overengineered, of course).

I guess different vendors see different user communities...

*************************************************************

From: Erhard Ploederedere
Sent: Wednesday, July 18, 2001, 6:56 AM

What I would like to see at some point is one write-up that summarizes the
pros and cons of the three or more approaches in a single document. With
three or more separate write-ups, these comparisons are harder to make.

I guess I am asking for a summarizing Study Note rather than multiple AIs,
which describe a single approach each and collect random comments in the
appendix. This study note may well be the eventual "master AI-171".

*************************************************************

From: Randy Brukardt
Sent: Wednesday, July 18, 2001, 2:26 PM

Sounds wonderful. Are you volunteering to write it? (So far, with this
proposal, the person who has floated an idea has been tagged to write it up.)

I know that the limited time I have available will prevent me from doing
anything like that until at least November. In the mean time, we're going to
have a meeting, and I need to rewrite several AIs (including the "type
separate" proposal). We're going to need something to discuss in the mean time.

If you want the simple version, here it is:

    "with type"
        Con: Can't handle access types (due to representation issues).
             Implementation needs "psuedo packages" internally.
             What happens when multiple paths to the same item "meet"?
        Pro: Easy to describe to users.
    "package abstract"
        Con: Heavy, complex mechanism. Complex visibility issues to work out.
             New kind of compilation unit affects all tools (alternatives
             avoiding compilation unit has similar effect on tools because the
             source must be compiled in two different ways).
        Pro: Handles access types cleanly.
    "type separate"
        Con: Must introduce extra packages (either "unnecessary" parents with
             Tucker's rules or "unrelated" ones with Dan's/Randy's rules).
             No allocators/deallocators on access types with designated types
             of this kind.
             (Probably others, once a proposal is made and picked apart.)
        Pro: No new kind of units. No new visibility issues. Access types can
             be handled (representation clauses can be given if needed). Model
             is easy to understand (it is an extension of existing rules). No
             run-time implementation problems; the implementation is identical
             to incomplete-completed-in-body.

*************************************************************

Questions? Ask the ACAA Technical Agent