[comp.lang.ada] HELP needed!!

klaiber@udel.EDU (Alexander Klaiber) (07/08/87)

HELP!!!!!
I am doing a high-level comparison of high-level languages and I've run
into a severe problem with Ada. What I am trying to do is the following:

Assume I have a generic package linked_list that, given some type "item",
defines a type "list" which is a list of "item".

I want to write a package that defined two types, A and B and operations on
these. Now my problem is that both A and B are records and A includes a
field of type "list of B" and B includes a field of type "list of A". I
want the lists to be handled by the generic linked_list. However, the
following obviously doesn't work:

	 package my_package is

	    type A is private;
	    type B is private;

	    package list_of_A is new linked_list(A);	-- ** problem **
	    package list_of_B is new linked_list(B);	-- ** problem **

	    -- operations on A/B and "list of A"/B go here

	 private
	    type A is record
		     xxx : list_of_A.list;
		  end record;
	    type B is record
		     xxx : list_of_B.list;
		  end record;
	 end my_package;

My compiler complains about A/B not being fully defined in the generic
instantiation of linked_list. As Ada doesn't seem to support cyclic import,
I see no way of splitting up this package.

Note that I always can define the "list of A/B" explicitly by using 
	type A is record
		  xxx : ptr_to_B_node;
	       end;
	(other type declarations here)

and writing procedures to handle the lists. However, this would (a) prevent
me from using the already existing generic package and (b) I would have to
include two copies of list-handlers, one for "lists of A" and one for
"lists of B". This would make Ada look unnecessarily bad. 

Any ideas how to work around this problem?

P.S. I haven't been programming in Ada all too long, so I may be
     overlooking the obvious...


Alexander Klaiber
klaiber@dewey.udel.edu

stt@ada-uts (07/10/87)

A solution is to pass an access-to-A as the generic actual type,
or include an access-to-list-header as the component of B (and move
the instantiations down below the full record type definitions).

However, your best solution is probably
to define A and B as access types themselves,
and defer the whole data structure definition to the package body
(see 3.8.1:3), as follows:

    type A is private;
    type B is private;
    . . .
private
    type A_Record;  -- full definition deferred to package body
    type A is access A_Record;
    type B_Record;  -- full definition deferred to package body
    type B is access B_Record;
end pkg;

package body pkg is
    <instantiations>
    type A_Record is
       B_List : List_Of_B.List;
     . . .

This also saves you greatly on recompilations when
you decide to add or change component definitions within
A_Record or B_Record.

Tucker Taft
c/o Intermetrics, Inc.
Cambridge, MA  02138

P.S. Here is a rationale for the restrictions...

Mutually recursive types clearly require the use of
access types somewhere.  You have hidden the use
within the generic.  This conflicts with the general
rule that the legality of an instantiation
is determined by the legality of the generic itself and
the legality of the actual/formal parameter matching.
(The only error which cannot be detected until the actual
parameter substitution takes place within the spec
and body of the generic has to do with the
use of unconstrained types (see 12.3.2:4).)

Allowing uncompleted private types as actual parameters as in your
example would severely increase the number of error situations which
could only be determined upon parameter substitution.
In your example it would be illegal if a list header included an instance
of the item type directly, whereas if a list header included only
an access type, then it would be legal.

cjh@petsd.UUCP (Chris Henrich) (07/15/87)

In article <322@louie.udel.EDU> klaiber@udel.EDU (Alexander Klaiber) writes:
>HELP!!!!!
>I am doing a high-level comparison of high-level languages and I've run
>into a severe problem with Ada. What I am trying to do is the following:
>
>Assume I have a generic package linked_list that, given some type "item",
>defines a type "list" which is a list of "item".
>
>I want to write a package that defined two types, A and B and operations on
>these. Now my problem is that both A and B are records and A includes a
>field of type "list of B" and B includes a field of type "list of A". I
>want the lists to be handled by the generic linked_list.
The following pattern is acceptable to at least one validated
compiler (that of Concurrent Computer Corp.):

PACKAGE my_package_1 IS
   TYPE a; -- "incomplete type declarations"
   TYPE b;

   TYPE a_ptr IS ACCESS a; -- about the only thing you can do with
   TYPE b_ptr IS ACCESS b; -- incomplete type declarations

   PACKAGE list_of_a_ptr IS NEW linked_list ( a_ptr );
   PACKAGE list_of_b_ptr IS NEW linked_list ( b_ptr );
 
   TYPE a IS
      RECORD
         xxx: list_of_b_ptr . list; 
      END RECORD;
   TYPE b IS
      RECORD
         xxx: list_of_a_ptr . list;
      END RECORD;
   --- more declarations, including operations on types "a" and "b"
END my_package_1;

Note that your linked lists come out as lists of pointers. 

This design is open to criticism, because it makes both the record
structure and the pointers visible to users of the package.  There is
some advantage to be gained from keeping the innards of "a" and "b"
private.  For one thing, increased freedom in using them to
instantiate other generic units. For another, it allows you to
separate the compilation of "a" and "b".  Here is a scheme that is a
little better from the viewpoint of software engineering:

PACKAGE my_package_2_a IS
   TYPE abstract_a IS PRIVATE;
   -- fundamental operations with abstract_a
PRIVATE
   TYPE record_a;
   TYPE abstract_a IS ACCESS record_a;
END my_package_2_a;

PACKAGE my_package_2_b IS
   TYPE abstract_b IS PRIVATE;
   -- fundamental operations with abstract_b
PRIVATE
   TYPE record_b;
   TYPE abstract_b IS ACCESS record_b;
END my_package_2_b;

-- note how the implementation of "a" depends on the *specification*
-- of "b"
WITH my_package_2_b; USE my_package_2_b;
WITH linked_list;
PACKAGE BODY my_package_2_a IS
   PACKAGE linked_list_b IS NEW linked_list ( abstract_b );
   TYPE record_a IS
      RECORD
         xxx: linked_list_b.list;
      END RECORD;
   -- lots of other implementation details
END my_package_2_a;

WITH my_package_2_a; USE my_package_2_a;
WITH linked_list;
PACKAGE BODY my_package_2_b IS
   PACKAGE linked_list_b IS NEW linked_list ( abstract_b );
   TYPE record_b IS
      RECORD
         xxx: linked_list_b.list;
      END RECORD;
   -- list of other implementaiton details
END my_package_2_b;

Hope this helps.

Regards,
Chris

--
Full-Name:  Christopher J. Henrich
UUCP:       ...!hjuxa!petsd!cjh
US Mail:    MS 313; Concurrent Computer Corporation;
            106 Apple St; Tinton Falls, NJ 07724
Phone:      (201) 758-7288
Concurrent Computer Corporation is a Perkin-Elmer company.