Version 1.2 of 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
!topic Private discrete types?
!reference AI95-00037, RM95 B.3, 7.3
!from Adam Beneschan 02-10-11
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
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.
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
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
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
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
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
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;
-- 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
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
... and so on.
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.
(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.)
package Interfaces.C is
type int is range <>;
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.
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.
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