[comp.lang.ada] layered packages

hasan@emx.utexas.edu (David A. Hasan) (02/16/91)

I am having some trouble building generic components in layers.
My motivation is to contruct layers as *independent* generic packages 
by WITHing low layers into higher layers and then adding additional 
features and "reexporting" some of the old features.  I would certainly
appreciate any insight you expreienced Ada folks can give me.

My question is best asked in the context of an example.  Suppose 
I have the following package which exports a primitive
ADT and some fundamental operations on the ADT:

   GENERIC
      TYPE item IS PRIVATE;
      TYPE coord IS (<>);
   PACKAGE grid IS
      TYPE adt IS PRIVATE;
      FUNCTION fetch( from_adt   : IN adt;
                      at_y, at_x : IN coord ) RETURN item;
      PROCEDURE store( the_item   : IN item;
                       in_adt     : IN OUT adt;
                       at_y, at_x : IN coord );
   PRIVATE
      TYPE adt IS ...whatever...;
   END grid;

"grid.adt" could be used for a number of things, but suppose I want 
to build a numerical matrix layer on top.  Here's my attempt:

   WITH grid;
   GENERIC
      TYPE scalar IS PRIVATE;
      -- WITH "+", "-" etc...
      TYPE index  IS (<>);
   PACKAGE matrix IS
      TYPE adt IS PRIVATE;
      FUNCTION fetch( from_adt       : IN adt;
                      at_row, at_col : IN index  ) RETURN scalar;
      PROCEDURE store( in_adt         : IN OUT adt;
                       at_row, at_col : IN index  );
      FUNCTION invert( the_matrix : IN adt ) RETURN adt;
      -- etc...
   PRIVATE
      PACKAGE low_level IS NEW grid(scalar,index);
      TYPE adt IS NEW low_level.adt;
      -- the derived type comes complete with  of low_level
      --    subprograms "fetch" and "store"
   END matrix;

How can one implement "matrix.fetch" and "matrix.store" so that
they are the *same* as the derived subprograms fetch and store visible at the 
end of the PRIVATE part of the package (i.e., how do I reexport them)?
For that matter, how can they be implemented to *call* the derived subprograms?

Should I perhaps use a SUBTYPE instead of a derived type?

----------------------------------------------------------------
: david a hasan -- Univ. of Texas Ctr. for Space Research
:               -- hasan@emx.cc.utexas.edu

madmats@elcgl.epfl.ch (02/16/91)

In article <44213@ut-emx.uucp>, hasan@emx.utexas.edu (David A. Hasan) writes:
> My question is best asked in the context of an example.  Suppose 
> I have the following package which exports a primitive
> ADT and some fundamental operations on the ADT:
> 
>    GENERIC
>       TYPE item IS PRIVATE;
>       TYPE coord IS (<>);
>    PACKAGE grid IS
>       TYPE adt IS PRIVATE;
>       FUNCTION fetch( from_adt   : IN adt;
>                       at_y, at_x : IN coord ) RETURN item;
>       PROCEDURE store( the_item   : IN item;
>                        in_adt     : IN OUT adt;
>                        at_y, at_x : IN coord );
>    PRIVATE
>       TYPE adt IS ...whatever...;
>    END grid;
> 
> "grid.adt" could be used for a number of things, but suppose I want 
> to build a numerical matrix layer on top.  Here's my attempt:
> 
>    WITH grid;
>    GENERIC
>       TYPE scalar IS PRIVATE;
>       -- WITH "+", "-" etc...
>       TYPE index  IS (<>);
>    PACKAGE matrix IS
>       TYPE adt IS PRIVATE;
>       FUNCTION fetch( from_adt       : IN adt;               ***
>                       at_row, at_col : IN index  ) RETURN scalar;
>       PROCEDURE store( in_adt         : IN OUT adt;
>                        at_row, at_col : IN index  );
-- I guess you meant:         ***
       PROCEDURE store( the_item       : in Scalar;            ***
                        in_adt         : IN OUT adt;
                        at_row, at_col : IN index  );
>       FUNCTION invert( the_matrix : IN adt ) RETURN adt;
>       -- etc...
>    PRIVATE
>       PACKAGE low_level IS NEW grid(scalar,index);
>       TYPE adt IS NEW low_level.adt;
>       -- the derived type comes complete with  of low_level
>       --    subprograms "fetch" and "store"

No, it does not because the derived subprograms fetch and sotre are
hidden by the ones declared above (see *** marks), because they
have the same parameter and result type profile.
If you want to call the original subprograms, you have to use type
conversions in order to call the subprograms that are declared in
the package instance low_level (see below).

>    END matrix;
> 
> How can one implement "matrix.fetch" and "matrix.store" so that
> they are the *same* as the derived subprograms fetch and store visible at the 
> end of the PRIVATE part of the package (i.e., how do I reexport them)?
> For that matter, how can they be implemented to *call* the derived subprograms?

package body Matrix is

       FUNCTION fetch( from_adt       : IN adt;
                       at_row, at_col : IN index  ) RETURN scalar is
       begin
          return
             Low_Level.Fetch(From_ADT => Low_Level.ADT(From_ADT),
                                         -- type conversion
                             At_X     => At_Row,
                             At_Y     => At_Col);
       end fetch;


       PROCEDURE store( the_item       : in Scalar;
                        in_adt         : IN OUT adt;
                        at_row, at_col : IN index  ) is
       begin
          Low_Level.Store(The_Item,
                          In_ADT => Low_Level.ADT(In_ADT),
                          At_X   => At_Row,
                          At_Y   => At_Col);
       end store;

end Matrix;

I think all this is awful because if you reuse a lower level ADT for 
implementing another one, operations that you redefine with the same
name are not written in the same way as the ones that are renamed.
The problem even becomes worse when the ADTs export generics (iterators
for example). At this point, please let me know if you still think
Ada does not need a *real* mechanism for inheritance.

> 
> Should I perhaps use a SUBTYPE instead of a derived type?

No you shouldn't because you can't: full declarations of private types
must be new types, distinct from all others; in particular, they can't
be subtypes.

Mats Weber

jls@yoda.Rational.COM (Jim Showalter) (02/18/91)

I submitted an Ada 9x on this, since I believe that at the point of
derivation you should now get matrix's fetch and store for FREE (this
is how it works in an inheritance language like C++) unless you want
to specifically override the inherited defaults.

Until such time as this is changed, what you do now is write stupid
little "skins" that basically just coerce the types from derived to
base and/or from base to derived, and call the base type's operations.
For example, here is matrix.fetch:

function fetch (from_adt : in adt;
                at_row, at_col : in index) return scalar is
begin
    return low_level.fetch (from_adt => low_level.adt (from_adt),
                           ....);
end fetch;

Barring this little annoyance, I think a derived type is better than
a subtype in this example.
--
***** DISCLAIMER: The opinions expressed herein are my own. Duh. Like you'd
ever be able to find a company (or, for that matter, very many people) with
opinions like mine. 
                   -- "When I want your opinion, I'll beat it out of you."