Ada: Packages

Ada: Packages

What are packages and how to use them.

Packages are a mechanism to organize Ada code. They allow us to:

  • Encapsulate related entities: We can group logically related declarations and definitions together in a package. This helps organize our code into logical units.

  • Share entities: Entities declared in a package specification can be shared and accessed by other program units.

  • Hide implementation: The package specification declares the entities that are visible outside the package. The package body contains the implementation which is hidden from outside. This enforces information hiding.

We organize code in packages in the following way:

  1. Create a package specification with package keyword:
package PACKAGE_NAME is
   -- declarations visible to the outside world
end PACKAGE_NAME;
  1. Create a package body with package body keyword:
package body PACKAGE_NAME is
   -- implementation 
end PACKAGE_NAME;
  1. The entities declared in the package specification are visible outside the package and can be used by other program units. The package body contains the implementation which is hidden.

  2. Related declarations can be grouped into logical packages to organize your code.


Using packages

You can include one package in another package in Ada using the with clause.

For example, if you have two packages:

  • PACKAGE1 specification and body

  • PACKAGE2 specification and body

And you want to use entities (types, constants, variables, subprograms) from PACKAGE1 in PACKAGE2, you can do:

In the PACKAGE2 specification:

package PACKAGE2 is
   with PACKAGE1;  -- Include PACKAGE1
   ...           
end PACKAGE2;

Now you can use the entities declared in PACKAGE1 within PACKAGE2. For example:

package body PACKAGE2 is
   Variable1 : PACKAGE1.Variable_Type;  -- Use variable from PACKAGE1
   ...  
end PACKAGE2;

The with clause makes the entities of the specified package visible within the current package. This allows modularization and reusability of code.

The with clause is specified in the specification of the package, not the body. The bodies of the packages do not need a with clause.


Subprogram Signature

A subprogram signature includes:

  • The subprogram name

  • The parameter names and modes (in, in out, out)

  • The parameter types

  • The return type (for functions)

It specifies the interface of the subprogram without defining the implementation.

The subprogram signature can be present in the package specification to expose the subprogram to other program units that use the package. The actual subprogram body is defined in the package body.

For example:

package PACKAGE_NAME is

   -- Subprogram signature   
   procedure PROC_NAME (Param1 : in INTEGER;  
                        Param2 : in out FLOAT);

   -- More declarations...

end PACKAGE_NAME;

package body PACKAGE_NAME is

   -- Subprogram body   
   procedure PROC_NAME (Param1 : in INTEGER;  
                        Param2 : in out FLOAT) is
   begin
      -- Implementation
   end PROC_NAME;

   -- More body...      

end PACKAGE_NAME;

Here the subprogram signature is defined in the package specification, making it visible outside the package. The actual subprogram body is defined in the package body.

This allows abstraction and information hiding, exposing only the interface of the subprogram to the outside world, hiding the implementation details.


Public members

Public members must be declared in the package specification in order to be visible and accessible to other program units that use the package.

Only the declarations in the package specification are visible outside of the package. The package body is not visible to other program units.

So for a member to be public and accessible, it must be declared in the package specification. This includes:

  • Types

  • Constants

  • Variables

  • Subprograms (signatures)

For example:

package PACKAGE_NAME is

   -- Public type   
   type Public_Type is ...;

   -- Public constant
   Public_Constant : constant Integer := 10;

   -- Public variable   
   Public_Variable : Integer;  

   -- Public subprogram signature
   procedure Public_Procedure;

end PACKAGE_NAME;

package body PACKAGE_NAME is

   -- Subprogram body   
   procedure Public_Procedure is
   begin
      ...
   end Public_Procedure;

end PACKAGE_NAME;

Here Public_Type, Public_Constant, Public_Variable and the subprogram Public_Procedure are all declared in the package specification, so they are public members and visible to other program units.

The package body only contains the implementation of Public_Procedure, but not its declaration.

So in summary, for a member to be public it must be declared in the package specification, not just defined in the package body.

Note. When a member is not declared in the specification is private member. If you need to make a private member public the only way is to duplicate the signature in the package specification. This is something specific to Ada.


Private:

The "private" keyword in Ada is used to declare private members within a package specification. It has the following main purposes:

  1. Information hiding: It hides the private members from other program units that use the package, exposing only the public interface. This achieves information hiding and encapsulation.

  2. Compiler checking: The Ada compiler can check that private members are only accessed within the package itself and not from outside the package. This helps catch errors.

  3. Flexibility: If a private member later needs to become public, we can simply move its declaration from the private section to the public section of the specification. This provides flexibility.

  4. Complete specification: The package specification contains all necessary declarations, including private members, to fully specify the interface of the package.

The "private" keyword is used as follows in a package specification:

package PACKAGE_NAME is

     -- Public declarations

     private

         -- Private declarations

end PACKAGE_NAME;

Anything declared after the "private" keyword is a private member that can only be accessed within the package body.

The benefits of private members are:

  • Information hiding

  • Compiler checking

  • Flexibility of implementation

  • Complete package specification

So in summary, the "private" keyword in Ada package specifications:

  • Declares private members that are hidden from other program units

  • Allows the compiler to check for proper encapsulation

  • Provides flexibility while maintaining a stable interface

  • Makes the package specification complete

Hope this explanation of the "private" keyword in Ada was helpful! Let me know if you have any other questions.


Public

The "private" and "public" keywords cannot alternate several times within an Ada package specification. They can only be used once, at the top level.

After the initial "public" section, if present, there must be exactly one "private" section. And after the "private" section, no further "public" or "private" sections are allowed.

The structure of an Ada package specification is:

  • Optional "public" section

  • Followed by required "private" section

  • No further "public" or "private" sections

So the keywords can only be used once, at the top level, to separate the initial public interface from the private implementation details:

package PACKAGE_NAME is

   public

      -- Public declarations

   private      

      -- Private declarations      

end PACKAGE_NAME;

Alternating "public" and "private" sections multiple times would not make sense and is not allowed by the language syntax.

The "public" section declares the elements of the interface that are visible to clients of the package.

The "private" section declares implementation details that are hidden from clients.

So alternating sections would break this concept of a clear separation between interface and implementation.


Standard Packages

There are many standard packages in the Ada language. These are packages that are part of the Ada language specification and are provided by all Ada compilers.

Some of the main standard packages in Ada are:

  • Ada.Text_IO: Provides routines for text input/output like read, write, put, get etc.

  • Ada.Integer_Text_IO: Provides text input/output for integer types.

  • Ada.Float_Text_IO: Provides text input/output for float types.

  • Ada.Calendar: Provides routines to work with dates and times.

  • Ada.Strings: Provides routines to manipulate strings.

  • Ada.Characters: Provides routines to manipulate characters.

  • Ada.Containers: Provides various container data structures like lists, maps, sets etc.

  • Ada.Exceptions: Provides routines to handle exceptions.

  • Ada.Numerics: Provides mathematical functions and number theoretic routines.

  • Ada.Sequential_IO: Provides routines for sequential file I/O.

  • Ada.Direct_IO: Provides routines for direct file I/O.

These and many other standard packages are defined in the Ada language specification and are included in all Ada compiler implementations.

They provide useful functionality like I/O, strings, containers, exceptions, numerical operations etc. so that programmers don't have to implement these from scratch.

The standard packages have a well-defined interface that is consistent across compilers, so code that uses the standard packages will be portable.

Programmers can use the standard packages in their Ada programs to perform common tasks, and know that their code will compile and run the same on any Ada compiler.

So the standard packages provide a useful library of routines that increase the usability and portability of the Ada language.


Package Manager

Currently, there is no comprehensive package manager for Ada similar to npm, pip, etc.

However, there are a few options for managing Ada library dependencies:

  1. GNATproject files - GNAT, the GNU Ada compiler, uses GNATproject files to specify library dependencies. A GNATproject file lists the dependent Ada spec files and body files. When you compile a project with GNAT, it will automatically compile all dependent library projects specified in the GNATproject file.

  2. ALI files - Ada Library (Object) files (.ali) are used to cache the results of compiling Ada specs. When a library changes, only the changed .ali files need to be recompiled. Ada projects can list .ali files as dependencies, and the compiler will only recompile changed .ali files.

  3. Makefiles - You can manage Ada library dependencies using Makefiles. Makefiles list the dependent Ada source files and library .ali files, and make can rebuild projects when dependencies change.

  4. Ada Controller - A commercial tool that can be used to manage complex Ada projects and library dependencies. It tracks which libraries a project depends on and can automatically recompile projects when dependencies change.

  5. AdaDev - Another commercial tool that provides a package manager for Ada. It allows installing Ada libraries from a central repository and manages library dependencies.

So while there is no single, dominant package manager for Ada like npm for JavaScript, there are some options for managing Ada library dependencies to some extent. But in general, dependency management in Ada is not as sophisticated as in other languages with dedicated package managers.


Disclaim: This article was created using Rix (AI)