7.3.1 Private Operations
For a type declared in the visible part of a package 
or generic package, certain operations on the type do not become visible 
until later in the package — either in the private part or the 
body. 
Such 
private operations are available 
only inside the declarative region of the package or generic package. 
 
Static Semantics
The predefined operators that exist for a given type 
are determined by the classes to which the type belongs. For example, 
an integer type has a predefined "+" operator. In most cases, 
the predefined operators of a type are declared immediately after the 
definition of the type; the exceptions are explained below. Inherited 
subprograms are also implicitly declared immediately after the definition 
of the type, except as stated below.
For a composite type, the characteristics (see 
7.3) 
of the type are determined in part by the characteristics of its component 
types. At the place where the composite type is declared, the only characteristics 
of component types used are those characteristics visible at that place. 
If later immediately within the declarative region in which the composite 
type is declared additional characteristics become visible for a component 
type, then any corresponding characteristics become visible for the composite 
type. Any additional predefined operators are implicitly declared at 
that place. If there is no such place, then additional predefined operators 
are not declared at all, but they still exist.
 
The corresponding rule applies to a type defined 
by a 
derived_type_definition, 
if there is a place immediately within the declarative region in which 
the type is declared where additional characteristics of its parent type 
become visible.
 
For 
example, an array type whose component type is limited private becomes 
nonlimited if the full view of the component type is nonlimited and visible 
at some later place immediately within the declarative region in which 
the array type is declared. In such a case, the predefined "=" 
operator is implicitly declared at that place, and assignment is allowed 
after that place.
 
  A type is a 
descendant 
of the full view of some ancestor of its parent type only if the current 
view it has of its parent is a descendant of the full view of that ancestor. 
More generally, at any given place, a type is descended from the same 
view of an ancestor as that from which the current view of its parent 
is descended. This view determines what characteristics are inherited 
from the ancestor, and, for example, whether the type is considered to 
be a descendant of a record type, or a descendant only through record 
extensions of a more distant ancestor.
 
  Furthermore, it is possible for there to be places 
where a derived type is known to be derived indirectly from an ancestor 
type, but is not a descendant of even a partial view of the ancestor 
type, because the parent of the derived type is not visibly a descendant 
of the ancestor.  In this case, the derived type inherits no characteristics 
from that ancestor, but nevertheless is within the derivation class of 
the ancestor for the purposes of type conversion, the "covers" 
relationship, and matching against a formal derived type. In this case 
the derived type is effectively a descendant of an incomplete 
view of the ancestor.
Inherited primitive subprograms follow a different 
rule. For a 
derived_type_definition, 
each inherited primitive subprogram is implicitly declared at the earliest 
place, if any, immediately within the declarative region in which the 
type_declaration 
occurs, but after the 
type_declaration, 
where the corresponding declaration from the parent is visible. If there 
is no such place, then the inherited subprogram is not declared at all, 
but it still exists. For a tagged type, it is possible to dispatch to 
an inherited subprogram that is not declared at all.
 
For a 
private_extension_declaration, 
each inherited subprogram is declared immediately after the 
private_extension_declaration 
if the corresponding declaration from the ancestor is visible at that 
place. Otherwise, the inherited subprogram is not declared for the private 
extension, though it might be for the full type. 
 
The Class attribute 
is defined for tagged subtypes in 
3.9. In addition, 
for every subtype S of an untagged private type whose full view is tagged, 
the following attribute is defined: 
 
S'Class
Denotes the class-wide subtype 
corresponding to the full view of S. This attribute is allowed only from 
the beginning of the private part in which the full view is declared, 
until the declaration of the full view. After the full view, the Class 
attribute of the full view can be used. 
 
9  Because a partial view and a full view 
are two different views of one and the same type, outside of the defining 
package the characteristics of the type are those defined by the visible 
part. Within these outside program units the type is just a private type 
or private extension, and any language rule that applies only to another 
class of types does not apply. The fact that the full declaration might 
implement a private type with a type of a particular class (for example, 
as an array type) is relevant only within the declarative region of the 
package itself including any child units.
The consequences of this actual implementation are, 
however, valid everywhere. For example: any default initialization of 
components takes place; the attribute Size provides the size of the full 
view; finalization is still done for controlled components of the full 
view; task dependence rules still apply to components that are task objects.
10  Partial views provide initialization, 
membership tests, selected components for the selection of discriminants 
and inherited components, qualification, and explicit conversion. Nonlimited 
partial views also allow use of 
assignment_statements.
 
11  For a subtype S of a partial view, S'Size 
is defined (see 
13.3). For an object A of 
a partial view, the attributes A'Size and A'Address are defined (see 
13.3). The Position, First_Bit, and Last_Bit 
attributes are also defined for discriminants and inherited components. 
 
Examples
Example of a type 
with private operations: 
package Key_Manager 
is
   type Key 
is private;
   Null_Key : 
constant Key; --
 a deferred constant declaration (see 7.4)
   procedure Get_Key(K : 
out Key);
   
function "<" (X, Y : Key) 
return Boolean;
private
   type Key 
is new Natural;
   Null_Key : 
constant Key := Key'First;
end Key_Manager;
 
package body Key_Manager is
   Last_Key : Key := Null_Key;
   procedure Get_Key(K : out Key) is
   begin
      Last_Key := Last_Key + 1;
      K := Last_Key;
   end Get_Key;
   function "<" (X, Y : Key) return Boolean is
   begin
      return Natural(X) < Natural(Y);
   end "<";
end Key_Manager;
12  Notes on the example: Outside 
of the package Key_Manager, the operations available for objects of type 
Key include assignment, the comparison for equality or inequality, the 
procedure Get_Key and the operator "<"; they do not include 
other relational operators such as ">=", or arithmetic operators.
The explicitly declared operator "<" 
hides the predefined operator "<" implicitly declared by 
the 
full_type_declaration. 
Within the body of the function, an explicit conversion of X and Y to 
the subtype Natural is necessary to invoke the "<" operator 
of the parent type. Alternatively, the result of the function could be 
written as not (X >= Y), since the operator ">=" is not 
redefined.
 
The value of the variable Last_Key, declared in the 
package body, remains unchanged between calls of the procedure Get_Key. 
(See also the NOTES of 
7.2.) 
 
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe