!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.] ****************************************************************