Subprograms and Modularity

Private subprograms

We've seen previously that we can declare private packages. Because packages and subprograms can both be library units, we can declare private subprograms as well. We do this by using the private keyword. For example:

    
    
    
        
private procedure Test;
procedure Test is begin null; end Test;

Such a subprogram as the one above isn't really useful. For example, we cannot write a with clause that refers to the Test procedure, as it's not visible anywhere:

    
    
    
        
with Test; procedure Show_Test is begin Test; end Show_Test;

As expected, since Test is private, we get a compilation error because this procedure cannot be referenced in the Show_Test procedure.

Private subprograms of a package

A more useful example is to declare private subprograms of a package. For example:

    
    
    
        
package Data_Processing is type Data is private; procedure Process (D : in out Data); private type Data is record F : Float; end record; end Data_Processing;
with Data_Processing.Calculate; package body Data_Processing is procedure Process (D : in out Data) is begin Calculate (D); end Process; end Data_Processing;
private procedure Data_Processing.Calculate (D : in out Data);
procedure Data_Processing.Calculate (D : in out Data) is begin -- Dummy implementation... D.F := 0.0; end Data_Processing.Calculate;
with Data_Processing; use Data_Processing; procedure Test_Data_Processing is D : Data; begin Process (D); end Test_Data_Processing;

In this example, we declare Calculate as a private procedure of the Data_Processing package. Therefore, it's visible in that package (but not in the Test_Data_Processing procedure). Also, in the Calculate procedure, we're able to initialize the private component F of the D object because the child subprogram has access to the private part of its parent package.

Private subprograms and private packages

We can also use private subprograms to test private packages. As we know, in most cases, we cannot access private packages in external clients — such as external subprograms. However, by declaring a subprogram private, we're allowed to access private packages. This can be very useful to create applications that we can use to test private packages. (Note that these applications must be library-level parameterless subprograms, because only those can be main programs.)

Let's see an example:

    
    
    
        
private package Private_Data_Processing is type Data is private; procedure Process (D : in out Data); private type Data is record F : Float; end record; end Private_Data_Processing;
package body Private_Data_Processing is procedure Process (D : in out Data) is begin D.F := 0.0; end Process; end Private_Data_Processing;
private procedure Test_Private_Data_Processing;
with Private_Data_Processing; use Private_Data_Processing; procedure Test_Private_Data_Processing is D : Data; begin Process (D); end Test_Private_Data_Processing;

In this code example, we have the private Private_Data_Processing package. In order to test it, we implement the private procedure Test_Private_Data_Processing. The fact that this procedure is private allows us to use the Private_Data_Processing package as if it was a non-private package. We then use the private Test_Private_Data_Processing procedure as our main application, so we can run it to test application the private package.

Child subprograms of private packages

We could also implement the Test subprogram that we use to test a private package P as a child subprogram of that package. In other words, we could write a procedure P.Test and use it as our main application. The advantage here is that this allows us to access the private part of the parent package P in the test procedure.

Let's rewrite the Test_Private_Data_Processing procedure from the previous example as the child procedure Private_Data_Processing.Test:

    
    
    
        
private package Private_Data_Processing is type Data is private; procedure Process (D : in out Data); private type Data is record F : Float; end record; end Private_Data_Processing;
package body Private_Data_Processing is procedure Process (D : in out Data) is begin null; end Process; end Private_Data_Processing;
procedure Private_Data_Processing.Test;
procedure Private_Data_Processing.Test is D : Data := (F => 0.0); begin Process (D); end Private_Data_Processing.Test;

In this code example, we now implement the Test procedure as a child of the Private_Data_Processing package. In this procedure, we're able to initialize the private component F of the D object. As we know, this initialization of a private component wouldn't be possible if Test wasn't a child procedure. (For instance, writing such an initialization in the Test_Private_Data_Processing procedure from the previous code example would trigger a compilation error.)