Ada's Serious Drawback: Package Names Cannot Be Used as Type Names

For developers adopting Ada, one of the most significant structural differences to overcome, especially when coming from other object-oriented languages, is how type hierarchies are organized.

Developers familiar with Java or C# are accustomed to a model where classes in the same package (namespace) can inherit from one another, forming a natural type hierarchy.

// A pseudo-code example in the style of Java/C#
// Types form an inheritance hierarchy within the 'widget' namespace.
package com.example.widget;

public class Widget { /*...*/ }
public class Window extends Widget { /*...*/ }
public class Button extends Widget { /*...*/ }
public class ToggleButton extends Button { /*...*/ }
public class CheckedButton extends Button { /*...*/ }

The structure where a base type Widget and its derived type Window coexist in the same namespace—also named Widget—is extremely common and intuitive. However, Ada has a fundamental limitation when it comes to expressing such a type hierarchy by name.

Hierarchy Comparison: The Intuitive vs. The Explicit

Let’s assume we are building a GUI toolkit and compare the hierarchical structure in both languages. ToggleButton and CheckedButton inherit from Button, while Button and Window inherit from Widget.

The Java / C# Approach: An Inheritance-Based Type Hierarchy

Within a single package (namespace), classes form a direct type hierarchy through inheritance. The type’s name itself represents the concept.

   Widget
   /     \
Window  Button
         /   \
        /     \
       /       \
ToggleButton  CheckedButton

The Ada Approach: A Package-Centric Hierarchy and the Inability to Use Package Names as Type Names

In Ada, everything must be separated into distinct packages (modules). To express the concept of Window inheriting from Widget, you must create a separate child package named Widget.Window. Furthermore, because a package name cannot be used as a type name, you must declare a separate type to hold the object instance. In this case, we will name that type Object.

     Widget
       |
     Object
     /   \
    /     \
Window   Button
   |       |
Object   Object
         /    \
        /      \
ToggleButton  CheckedButton
     |              |
   Object         Object
-- Example of variable declarations, showing the required '.Object' suffix.
-- Note: 'with' and 'use' clauses can be used to omit parts of the namespace.
my_widget        : access Widget.Object;
my_window        : access Widget.Window.Object;
my_button        : access Widget.Button.Object;
my_toggle_button : access Widget.Button.ToggleButton.Object;

The necessity of writing Widget.Window.Object where one might expect Widget.Window results in a verbose structure that can appear cumbersome.

Conclusion: The Trade-Off for Reliability

This strict design philosophy in Ada can be a point of friction. The code becomes more verbose, and developers must forgo some of the more intuitive object-oriented patterns found in other languages.

However, this design is not arbitrary; it is a deliberate trade-off. This structural choice is often accepted because Ada provides unparalleled guarantees of safety and reliability. The language’s rigorous compile-time validation and powerful, built-in run-time checks continuously verify program correctness. For these guarantees, the verbosity and structural explicitness are considered a worthwhile price.