Version 1.2 of acs/ac-00052.txt

Unformatted version of acs/ac-00052.txt version 1.2
Other versions for file acs/ac-00052.txt

!standard 7.3(00)          03-01-23 AC95-00052/01
!class amendment 03-01-23
!status received no action 02-10-11
!subject Private discrete types
!summary
!appendix

!topic Private discrete types?
!reference AI95-00037, RM95 B.3, 7.3
!from Adam Beneschan 02-10-11
!discussion

I ran across this old comment from Keith Thompson regarding
Interfaces.C, in AI95-00037:

>> May the type Interfaces.C.Char be declared as
>>     type char is new Character;
>> ?
>>
>> May Interfaces.C.wchar_t be declared as
>>     type wchar_t is new Wide_Character;
>> ?
>>
>> If so, type conversions between Character and char, and between
>> Wide_Character and wchar_t, are legal under some implementations and
>> not under others.

Also, using character literals for the wchar_t type would be legal
under some implementations and not others.

>> Given the wording in the RM, I believe the answer is yes in both cases.
>> This is an interesting source of quietly non-portable programs.

This could be solved by making the type wchar_t private, but that make
many operations illegal that are currently legal (such as
wchar_t'first, 'succ, etc.)

What I'm wondering is: Has the notion of a "discrete private" type
been discussed before?  If so, did it seem potentially useful, too
esoteric, too difficult to implement, or what?  I don't see any AI
that deals with this.

The idea is that you could define something like

    type T is private (<>);

(or, to be really consistent with generics:)

    type T is (<>);

where the full view of T would have to be a discrete type.  The
partial view of T would then have all the predefined attributes
available to discrete types (3.5(10-55), 3.5.5), and could be used
anywhere the language requires a discrete or scalar type (e.g. in a
FOR loop, in the index part of an array definition, as an actual
for a generic formal discrete type, etc.).

My first guess is that implementing this new feature wouldn't be
terribly difficult, since the compiler would do a lot of the same
things it already has to do for generic formal discrete types.

If this declaration were available and were used in Interfaces.C, this
would address Keith's objection and prevent certain nonportable
operations from being used on wchar_t, while still allowing all the
operations normally available for discrete types.  I imagine it might
be useful in other situations too, but I don't know for sure.

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

!topic Proposal for "semi-private" types
!reference RM95-3.2.1, RM95-12.5(2)
!from Niklas Holsti 03-09-26
!keywords private types, generic formals, client-server contracts
!discussion

Summary

   In addition to the current "fully private" private types, it should
   be allowed to declare types that are "partially private" in the
   same sense and same syntax as for generic formal type parameters.

   For example, in the public part of a package declaration we could
   write 'type T is (<>);' or 'type T is range <>;' to inform the
   clients that the type T is a discrete type or a signed integer
   type without revealing full details such as the actual enumeration
   literals or numerical ranges. The full declaration would be in the
   private part of the package declaration, as for fully private types.


Existing similar constructs

   1. Generic formal type parameters.


Similar or related other proposals

   1. Being able to declare 'type T is tagged;' so that T'Class can
      be used before the full declaration of T.


Expected impact on compilers

   Changes to the type-visibility and freezing definitions. However,
   similar rules already exist for generics. Code generation for a
   package declaration may be affected, because the full definition of
   some types is not visible until the private part is compiled. No
   difference in code generation for the body of a package, because
   the type is then fully defined.


Motivation

   When an application is composed from software components, possibly
   implemented by different parties or with a variety of
   implementations for different platforms or variants of the
   application, there is a kind of contract relationship between
   "server" packages (the ones that provide types and operations) and
   their "client" packages (the ones that use these types and
   operations). This proposal focuses on the role of types in this
   contract.

   The server packages promise to provide certain types with certain
   properties, and the client packages are designed to rely on the
   existence and properties of these types. It is desirable for the
   contract to be flexible so that the clients rely only on a minimal
   and well defined set of type properties. This makes it possible to
   change server implementations without forcing changes in the
   clients. Just recompiling and relinking the application will be
   enough.

   Server implementations may need to vary depending on the target
   platform, the geographic location, the specific variant of the
   application and other similar reasons.

   Contract flexibility is currently achieved mainly by hiding data
   and type structures. The servers provide abstract data types
   instead of concrete ones. Currently in Ada, there are only three
   choices for exposing or hiding a type:

      (1) the type is fully public;
      (2) the type is fully private; or
      (3) the type has some private parts and some public parts.

   The last choice only applies to record and tagged types, where
   some components may be of a private type and others of a public
   type, and to array types where the index type may (and must) be
   public while the component type can be private or a record with
   some private and some public parts.

   We will here ignore the third choice because it only amounts to
   an aggregation of lower-level types where at the bottom level
   only the first two choices (fully public or fully private) are
   possible.

   The problem is that if the type is fully public, its declaration
   may reveal properties that are not meant to be part of the contract.

   For example, assume that the contract means to define a discrete
   type that can be used as an array index. If this type is declared
   as a public type, the declaration must reveal whether the type is
   an enumerated type, a signed integer type or a modular integer type.
   It must also reveal the enumeration literals, the parent type if
   any,  or the actual integer ranges, as the case may be.

   The clients may inadvertently come to rely on properties of the type
   that are not meant to be in the contract and actually vary across
   different server implementations, making the clients incompatible
   with some server implementations. This is bad, since it limits the
   reusability, maintainability and portability of the components and
   the entire application.

   On the other hand, if the type is fully private then the clients
   can only use the type through server operations. Basic attributes
   such as 'First and 'Last must be provided through server functions,
   and no private type can be used as an array index or as the discrete
   subtype in a "for .. loop" statement. This makes it harder to design
   and implement applications that use private types, which leads to
   an undesirable trade-off between short-term concerns (use public
   types to achieve quick and cheap design and coding, fast execution,
   small code size) and long-term concerns (use private types to help
   maintenance, reuse, porting).

   The proposed solution is inspired by the contract between a generic
   and its users (instantiators), which is very similar to the contract
   between servers and clients:

   - The generic formals part corresponds to the contract. It reveals
     only some of the properties of the types in the contract.

   - The rest of the generic corresponds to the client. It is
     allowed to use only those type properties that were revealed
     in the formals part.

   - An instantiation of the generic corresponds to an implementation
     of the server. It provides concrete types that have the properties
     revealed and required in the contract. The types also have other
     properties, but the generic mechanism guarantees that the precise
     nature of these other properties is irrelevant to the proper
     working of this instance of the generic.

   The proposal is simply this: Ada should allow, in the public part
   of a package declaration, some of the same kinds of incomplete or
   partially revealing type declarations as in generic formal type
   parameters.

   Any package that "withs" this package would then have the same
   knowledge about the declared entities as a generic unit has about
   its formals. However, as for the current fully private types, the
   full declarations would have to be stated in the private part of
   the package declaration, so the compiler would still, after
   compiling the package declaration, have full knowledge of the
   declared entities.

   Only the analogues of formal type declarations are proposed.
   The other forms of generic formal parameter declarations are not
   needed in this context because ordinary declarations can be used
   instead; it is only the "partially private" aspect of formal types
   that is currently missing.


Example 1: Bongo-drum scheduling

   package Bongo_Drum_Scheduling is
   -- Provides thread scheduling according to the bongo-drum
   -- method. The properties of this method may depend on the
   -- processing platform, viz. how many drums there are, how
   -- far the drum-beaters can count priorities, and how many
   -- dancing threads can fit in the circle.

      type Drum_Index is (<>);
      -- Identifies a drum. You can use the fact that it is
      -- a discrete type, but you can and must use 'First and
      -- 'Last to find out the range, because it is private.

      type Priority is range <>;
      -- The priority of a thread. You can rely on it being an
      -- integral type, so you can use addition and subtraction,
      -- but you can and must use 'First and 'Last to find out
      -- how many priority levels there are.

      type Circle_Position is mod <>;
      -- The position of a dancing thread in the circle around the
      -- bongo players. You know it is a circle, but you must
      -- use 'Modulus or 'Last to find out the circumference
      -- (number of dancer positions).

      type Thread is new Controlled with private;
      -- The thread type is controlled, just so you know,
      -- but I am not showing you the extension, not even as
      -- a private type.

      type Beat is record
         Numerator   : Circle_Position;
         Denominator : Circle_Position;
      end record;
      -- Defines one component of the dancing/scheduling rhythm.

      type Rhythm is array (Drum_Index) of Beat;
      -- Bom-bom bom-bom-bom, tap tap, scratch and skip.
      -- You know a lot about this type:
      --  o  It is a one-dimensional constrained array.
      --  o  Its component type is a record with two
      --     components named Numerator and Denominator,
      --     both of the modular type Circle_Position.
      -- You also _do not_ know many things:
      -- o   The array length (you can and must use 'Length)
      -- o   The index range (you can and must use 'Range or
      --     'First or 'Last)
      -- o   The modulus of Circle_Position.

      ... operation declarations omitted

   private

      type Dum_Index is (Tiny, Big, Medium);
      -- In this set, we have three drums.

      type Priority is new Natural;
      -- We have lots of priorities.

      type Circle_Position is mod 9;
      -- Nine threads fit in the circle.

      type Thread is new Controlled with record
         ... new components
      end record;

      ... and so on.

   end Bongo_Drum_Scheduling;


Example 2: Some Ada predefined packages.

   The proposed extension could be used in the LRM itself, to
   make the public parts of the predefined packages real Ada
   and hide the "implementation defined" remarks in the private
   parts. For example:

      package Standard is
         ...
         type Integer is range <>;
         -- Shows that Integer is a signed integer type, but
         -- the actual bounds are private.

         type Float is digits <>;
         -- Shows that Float is a floating-point type, but the
         -- actual precision is private.
         ...
      private
         implementation-defined
      end Standard;

   (Note: RM95-A.1(48) states that "Standard has no private part".
   I do not know if this is a deep requirement or merely a note
   about the current definition of Standard.)

   Another example:

      package Interfaces.C is
         ...
         type int is range <>;
         ...
      private
         implementation-defined
      end Interfaces.C;


Work-around designs

   The server-client type-contract can be implemented in other ways,
   of course. Some of these ways are:

   1. Use fully public types and separate the intentionally public
      and accidentally public properties in documentation or comments.
      This does not give a compile-time check that the clients use
      only the intentionally public (contracted) type properties.

   2. Use fully private types and implement all the necessary
      operations on these types. This is limited by the inability to
      overload assignment, indexing and literals.

   3. Write all the client packages as generic packages with all
      the server-provided items as generic formals. This is quite
      cumbersome and prevents any bidirectional interaction between
      clients and servers. where the servers use some items provided
      by the clients, for example composite types that combine
      items from servers and clients or call-back operations that
      let servers activate clients. Such bidirectional interaction
      is usually necessary, in my experience.


Possible extensions

   Perhaps a wider syntax than in generics could be used, with more
   options for revealing and hiding properties. For example:

      type Apples_Count is new Natural range <>;
      -- Apples are counted with a type derived from Natural,
      -- but we are not showing the range, so use 'First and 'Last.

      type Positive_Count is range 1 .. <>;
      -- A count, starting from 1, with a private upper bound.

      subtype Index is Integer range <>;
      -- A subtype of Integer, with private bounds.


Possible problems

   The current freezing rules may imply that some types must be
   fully defined before the private part is compiled. For example,
   if a type declared as "type Num is range <>" is then used as the
   index type in an array declaration, the compiler may not know
   how to implement the array type because the 'Size of the index
   type is not known yet, being defined in the private part.

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

From: Adam Beneschan
Sent: Friday, September 26, 2003  3:34 PM

Deja vu all over again?  I posted the following to Ada-Comment on
October 11, 2002.  I was later told that this was essentially the same
as Ada9X revision request #229, submitted on July 21, 1989.

[Editor's note: see above.]

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

Questions? Ask the ACAA Technical Agent