Skip to content

3. Ada

1. Introduction

Ada is a high-level, statically typed, and structured programming language developed in the late 1970s under the U.S. Department of Defense. Its primary goal was to provide a language suited for large-scale, long-lived, high-integrity, and mission-critical applications, especially in embedded systems, real-time systems, and safety-critical systems. Ada emphasizes reliability, safety, maintainability, and correctness.

C, developed in the early 1970s, is a general-purpose, procedural programming language that is widely used for system programming, embedded systems, and application development. C is known for its performance, efficiency, and close-to-hardware capabilities, but it lacks some of the safety and correctness features that Ada emphasizes.

1.1 Comparison of Ada and C:

FeatureAdaC
Design PurposeDesigned for high-reliability, safety-critical systems, especially in real-time and embedded domains.Designed for system-level programming with a focus on performance and efficiency.
Type SystemStrongly typed, with strict type checking and user-defined types.Weakly typed (compared to Ada), allowing implicit conversions between types.
Safety and Error PreventionAda has extensive features for preventing programming errors: strong typing, range checks, overflow detection, and package encapsulation.C provides minimal error prevention mechanisms, and issues like buffer overflows, null pointer dereferences, and memory leaks are common risks.
Concurrency SupportAda has built-in concurrency with tasks, protected objects, and rendezvous mechanisms for communication and synchronization.C does not have built-in concurrency support; external libraries like POSIX threads (pthreads) are used for multithreading.
Exception HandlingAda has robust, built-in exception handling with the raise and exception blocks.C lacks built-in exception handling; errors are typically handled through return codes or libraries (like setjmp and longjmp).
Memory ManagementAda supports both automatic and manual memory management. It includes strong features to avoid memory-related errors.C requires manual memory management through malloc, free, and pointer arithmetic, which increases the risk of memory leaks and corruption.
Modularity and PackagesAda uses packages for modular programming, which encapsulate data and subprograms, supporting better abstraction and information hiding.C uses header files and source files for modularity but lacks the strong encapsulation features of Ada packages.
Generics (Parametric Polymorphism)Ada supports generics, allowing the creation of reusable code components.C does not have built-in support for generics; this is typically done using macros or manual code duplication.
Object-Oriented SupportAda supports object-oriented programming (OOP) with classes, inheritance, and polymorphism, but it’s optional.C is not object-oriented, though OOP can be simulated using structures and function pointers. C++ (a superset of C) adds object-oriented features.
Code Readability and MaintainabilityAda emphasizes readability and maintainability with clear syntax and strong typing, making it easier to write reliable code.C prioritizes performance, and its flexibility allows developers to write efficient but sometimes cryptic and hard-to-maintain code.
Compiler/Tool SupportGNAT is the most common Ada compiler. Ada also has static analysis tools, runtime checks, and formal verification support.GCC and Clang are popular compilers for C, with broad toolchain support, but C does not have as many built-in correctness tools as Ada.
PerformanceAda programs can be optimized for performance but tend to have more runtime checks that might slow execution. However, these can be turned off in non-critical deployments.C is known for its performance and efficiency, with direct access to hardware features, but at the cost of safety and error detection.
SyntaxAda syntax is more verbose and structured, which improves clarity but can be more difficult for beginners.C syntax is compact and less verbose, making it easier to write small programs but potentially harder to maintain in large projects.
Community and EcosystemAda has a smaller community, primarily used in aerospace, defense, and critical systems.C has a massive global community, with vast libraries, frameworks, and support across all domains of programming.
Use CasesUsed in mission-critical systems like aerospace, defense, air traffic control, medical devices, and railway systems.Used in system-level programming, operating systems, embedded systems, and performance-critical applications.

1.2 Key Differences:

  • Reliability vs. Performance: Ada is designed to prioritize reliability, maintainability, and safety, making it ideal for mission-critical applications where correctness is more important than raw performance. C, on the other hand, prioritizes performance and efficiency, making it better suited for system-level programming where fine control over hardware is needed.

  • Error Prevention: Ada includes more built-in error prevention mechanisms, such as strong typing and bounds checking, while C gives the programmer more freedom but requires more caution to avoid errors like buffer overflows and memory leaks.

  • Concurrency: Ada has built-in support for concurrent programming, which is highly structured and safer than C’s reliance on external libraries for concurrency.

2. Getting Started

To get started with Ada on Ubuntu, you need to install an Ada compiler and set up your environment. The most commonly used Ada compiler is GNAT, which is part of the GNU Compiler Collection (GCC).

2.1 Install GNAT (GNU Ada Compiler)

GNAT is included in the official Ubuntu repositories, so you can install it easily using the package manager.

  • Open a terminal and run the following command to install the GNAT compiler:

    Terminal window
    sudo apt update
    sudo apt install gnat

2.2 Verify the Installation

  • Once the installation is complete, verify that GNAT is correctly installed by checking the version:

    Terminal window
    gnat --version

    You should see output similar to:

    GNAT 10.2.0 (or another version depending on your system)

2.3 Create a Simple Ada Program

  • Create a simple Ada program using any text editor. Here’s an example program called hello.adb:
-- hello.adb
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
begin
Put_Line ("Hello, Ada!");
end Hello;

2.4 Compile the Ada Program

  • Use the gnatmake command to compile the Ada program:
Terminal window
gnatmake hello.adb

This command compiles the program and generates an executable file named hello.

2.5 Run the Executable

  • To run the program, execute the compiled file:
Terminal window
./hello

The output should be:

Hello, Ada!

2.6 Set Up an Ada Development Environment (Optional)

  • You can use text editors like Visual Studio Code or Emacs with Ada language support to improve your development experience.

  • For Visual Studio Code: 2. Install VS Code:

    Terminal window
    sudo snap install --classic code
    1. Install the Ada Language Support extension in VS Code.

2.7 Compiling Larger Projects

  • GNAT uses project files (.gpr) for larger projects. You can create and manage project files for better organization as your Ada projects grow.

Example of a simple project file hello.gpr:

Terminal window
project Hello is
for Source_Dirs use (".");
for Main use ("hello.adb");
end Hello;

You can compile a project using:

Terminal window
gprbuild hello.gpr

That’s it! You’ve now installed GNAT on Ubuntu and are ready to start programming in Ada.

3. Data Structures in Ada

Ada provides a variety of data structures to support both simple and complex programming needs. These structures emphasize reliability and strong typing, making Ada well-suited for critical applications. Here are some key data structures in Ada:

3.1 Scalar Types

  • Integer: Represents whole numbers. You can define ranges, e.g., type Small_Int is range 0..100;.
  • Float: Represents real numbers with a floating-point.
  • Boolean: Represents True or False values.
  • Character: Represents individual characters.

3.2 Composite Types

These include types that are made up of multiple components.

  • Arrays: Collections of elements indexed by integers or other types. Arrays can be static or dynamic.

    type Int_Array is array (1 .. 10) of Integer;

    Ada also supports multi-dimensional arrays:

    type Matrix is array (1 .. 3, 1 .. 3) of Integer;
  • Records: Similar to structs in C, they group related data fields of different types.

    type Person is record
    Name : String(1..10);
    Age : Integer;
    end record;
  • Unions (Variant Records): A record type that can hold different fields depending on the value of a discriminant.

    type Shape (Kind : Boolean) is record
    case Kind is
    when True => Radius : Float;
    when False => Length, Width : Float;
    end case;
    end record;

3.3 Access Types (Pointers)

Access types are Ada’s version of pointers, but they are safer than C/C++ pointers, avoiding common errors such as dereferencing null pointers or pointer arithmetic.

type Node;
type Node_Ptr is access Node;
type Node is record
Value : Integer;
Next : Node_Ptr;
end record;

3.4 Tagged Types and Object-Oriented Structures

Ada supports object-oriented programming (OOP) with tagged types, which are similar to classes in other languages.

type Animal is tagged record
Name : String(1..10);
end record;
type Dog is new Animal with record
Breed : String(1..10);
end record;

3.5 Protected Types

Used in concurrent programming for data protection in multi-threaded applications. Protected types provide safe access to shared resources.

protected Counter is
procedure Increment;
function Value return Integer;
private
Count : Integer := 0;
end Counter;

3.6 Generic Types

Ada allows generic programming, where you can define generic data structures (such as a stack or queue) that can work with any type.

generic
type Element_Type is private;
package Generic_Stack is
procedure Push (Item : Element_Type);
function Pop return Element_Type;
end Generic_Stack;

3. Control Structures in Ada

Ada’s control structures are designed to enforce clear and structured programming, ensuring that the flow of control is well-defined and easy to understand.

3.1 Sequential Control

Ada follows typical sequential execution, where statements are executed one after the other.

3.2 Conditional Control

  • If Statements: Ada supports conditional branching with the if statement.

    if X > Y then
    Put_Line("X is greater");
    elsif X = Y then
    Put_Line("X is equal");
    else
    Put_Line("X is less");
    end if;
  • Case Statements: A multi-way branch based on the value of an expression.

    case Day is
    when Monday => Put_Line("Start of week");
    when Tuesday => Put_Line("Second day");
    when others => Put_Line("Another day");
    end case;

3.3 Looping Structures

Ada provides several looping constructs to handle repetition.

  • While Loop: A pre-condition loop.

    while X < 10 loop
    X := X + 1;
    end loop;
  • For Loop: A loop that iterates over a range of values.

    for I in 1 .. 10 loop
    Put_Line(Integer'Image(I));
    end loop;
  • Loop with Exit: A general loop construct that can be exited based on a condition.

    loop
    exit when X > 10;
    X := X + 1;
    end loop;

3.4 Concurrency Control

Ada is well-known for its support for concurrent programming with tasks and protected objects.

  • Tasks: Ada’s built-in support for parallelism.

    task type Worker_Task is
    entry Start_Work;
    end Worker_Task;
    task body Worker_Task is
    begin
    accept Start_Work;
    -- Do some work
    end Worker_Task;
  • Protected Objects: These provide safe access to shared resources in concurrent programs.

    protected Shared_Data is
    procedure Add(Value : Integer);
    function Get return Integer;
    private
    Data : Integer := 0;
    end Shared_Data;

3.5 Exception Handling

Ada has robust exception handling features to catch and handle runtime errors.

begin
X := 10 / 0; -- Will raise an exception
exception
when Constraint_Error => Put_Line("Division by zero!");
end;

Ada’s strong emphasis on safety, reliability, and maintainability sets it apart from many other languages. It incorporates features like exception handling, concurrency, and data encapsulation into its core, making it ideal for high-integrity systems.