Beginner

Hello World in Ada

Your first Ada program - the classic Hello World example with Docker setup using GNAT

Every programming journey starts with Hello World. Let’s write our first Ada program using the GNAT compiler (GNU Ada).

The Code

Create a file named hello.adb:

1
2
3
4
5
6
with Ada.Text_IO;

procedure Hello is
begin
   Ada.Text_IO.Put_Line ("Hello, World!");
end Hello;

Understanding the Code

Ada programs are clean, readable, and structured. Let’s break down each part:

  • with Ada.Text_IO; - Imports the text input/output package
  • procedure Hello is - Declares a procedure named Hello (the program entry point)
  • begin - Marks the start of executable statements
  • Ada.Text_IO.Put_Line ("Hello, World!"); - Outputs text with a newline
  • end Hello; - Closes the procedure (must match the procedure name)

The With Clause

The with clause makes a package visible to your program. Ada.Text_IO is Ada’s standard I/O library, similar to stdio.h in C or iostream in C++. Unlike many languages, Ada requires explicit imports - there are no implicit global namespaces.

Procedure Declaration

Ada programs begin execution at a parameterless procedure. The structure is:

1
2
3
4
5
procedure Name is
    -- declarations go here
begin
    -- statements go here
end Name;

Notice the semicolons - they terminate statements, not separate them. The end clause includes the procedure name, providing redundancy that helps catch errors.

Case Insensitivity

Ada is case-insensitive for keywords and identifiers. These are all equivalent:

1
2
3
with Ada.Text_IO;
WITH Ada.Text_IO;
With ada.text_io;

However, Ada style guides recommend:

  • Keywords in lowercase: with, procedure, begin, end
  • Package names in Mixed_Case: Ada.Text_IO
  • Constants in ALL_CAPS: MAX_SIZE
  • Variables in Mixed_Case: Counter, Total_Amount

Running with Docker

The easiest way to run Ada without installing GNAT locally uses GCC (which includes GNAT) in a Docker container:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Create the source file
cat > hello.adb << 'EOF'
with Ada.Text_IO;

procedure Hello is
begin
   Ada.Text_IO.Put_Line ("Hello, World!");
end Hello;
EOF

# Compile and run with GNAT
docker run --rm -v $(pwd):/app -w /app codearchaeology/ada:latest sh -c 'gnatmake hello.adb && ./hello'

Understanding the Docker Command

  • gnatmake - The GNAT build tool (handles compilation and linking)
  • hello.adb - Input source file (.adb = Ada body)
  • ./hello - Run the compiled executable

The gnatmake command automatically:

  1. Compiles the source to object code
  2. Binds Ada-specific runtime information
  3. Links with the Ada runtime library
  4. Produces an executable

Running Locally

If you have GNAT installed:

1
2
3
4
5
# Compile and link
gnatmake hello.adb

# Run
./hello

Installing GNAT

macOS (Homebrew):

1
2
3
4
5
# GNAT is part of GCC
brew install gcc

# Verify installation
gnatmake --version

Ubuntu/Debian:

1
sudo apt install gnat

Windows: Download from AdaCore Community Edition or use MSYS2:

1
pacman -S mingw-w64-x86_64-gcc-ada

Fedora/RHEL:

1
sudo dnf install gcc-gnat

Expected Output

Hello, World!

Clean and simple - exactly what you’d expect!

Alternative Approaches

Ada’s standard library offers several ways to output text:

Using Use Clause for Brevity

1
2
3
4
5
6
with Ada.Text_IO; use Ada.Text_IO;

procedure Hello is
begin
   Put_Line ("Hello, World!");
end Hello;

The use clause brings all of Ada.Text_IO into scope, so you can call Put_Line directly instead of Ada.Text_IO.Put_Line. This is more convenient but less explicit.

Using Put Instead of Put_Line

1
2
3
4
5
6
7
with Ada.Text_IO;

procedure Hello is
begin
   Ada.Text_IO.Put ("Hello, World!");
   Ada.Text_IO.New_Line;
end Hello;

Put outputs text without a newline. New_Line adds the newline explicitly.

With Declarations Section

1
2
3
4
5
6
7
with Ada.Text_IO;

procedure Hello is
   Message : constant String := "Hello, World!";
begin
   Ada.Text_IO.Put_Line (Message);
end Hello;

Variables and constants are declared between is and begin.

The .adb and .ads Files

Ada programs use two file types:

.adb (Ada Body)

Contains:

  • Procedure/function implementations
  • Main program code
  • Package bodies

.ads (Ada Specification)

Contains:

  • Package interfaces
  • Type declarations
  • Subprogram specifications

Our simple Hello World only needs a .adb file since it’s a standalone procedure. As programs grow, you’ll create packages with separate specification (.ads) and body (.adb) files.

Program Structure

A minimal Ada program requires:

  1. Optional with clauses - Import needed packages
  2. Procedure declaration - Define the entry point
  3. Optional declarations - Variables, constants, types
  4. Begin keyword - Start executable code
  5. Statements - Actual program logic
  6. End clause - Close the procedure

Example with Declarations

1
2
3
4
5
6
7
8
9
with Ada.Text_IO;

procedure Greeting is
   Name : String := "Ada Lovelace";
   Year : Integer := 1815;
begin
   Ada.Text_IO.Put_Line ("Hello from " & Name);
   Ada.Text_IO.Put_Line ("Born in" & Integer'Image (Year));
end Greeting;

Notice:

  • Variables declared between is and begin
  • & concatenates strings
  • Integer'Image converts integers to strings (Ada attributes use single quote)

Compilation Process

When you run gnatmake hello.adb, several things happen:

hello.adb → hello.ali (Ada Library Information)
         → hello.o (Object file)
         → hello (Executable)

The .ali Files

GNAT creates .ali (Ada Library Information) files containing:

  • Dependency information
  • Compilation options
  • Source checksums
  • Cross-reference data

These enable fast incremental compilation and consistency checking.

Separate Compilation Steps

You can also compile manually:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Compile to object
gcc -c hello.adb

# Bind Ada code
gnatbind hello.ali

# Link to executable
gnatlink hello.ali

# Or let gnatmake handle everything
gnatmake hello.adb

Most developers use gnatmake or the more modern gprbuild tool.

Common Beginner Mistakes

Missing Semicolons

1
2
Ada.Text_IO.Put_Line ("Hello")   -- ❌ Missing semicolon
Ada.Text_IO.Put_Line ("Hello");  -- ✅ Correct

Mismatched End Name

1
2
3
4
5
6
7
8
9
procedure Hello is
begin
   Ada.Text_IO.Put_Line ("Hello, World!");
end Goodbye;  -- ❌ Must match procedure name

procedure Hello is
begin
   Ada.Text_IO.Put_Line ("Hello, World!");
end Hello;    -- ✅ Correct

Forgetting With Clause

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
procedure Hello is
begin
   Put_Line ("Hello, World!");  -- ❌ Ada.Text_IO not imported
end Hello;

with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
begin
   Put_Line ("Hello, World!");  -- ✅ Correct
end Hello;

Wrong File Extension

Ada is case-insensitive, but file extensions matter:

  • hello.ada - ❌ Not recognized by GNAT
  • hello.adb - ✅ Correct for program body
  • hello.ads - ✅ Correct for specification

Compiler Options

Useful GNAT compilation flags:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Enable all warnings
gnatmake -gnatwa hello.adb

# Treat warnings as errors
gnatmake -gnatwe hello.adb

# Generate debugging information
gnatmake -g hello.adb

# Optimize for speed
gnatmake -O2 hello.adb

# Verbose output (see all compilation steps)
gnatmake -v hello.adb

# Check syntax only (no code generation)
gcc -c -gnats hello.adb

Style Checking

1
2
3
4
5
6
# Enable style checks
gnatmake -gnatyy hello.adb

# Specific style checks
gnatmake -gnatya hello.adb  # Check attribute casing
gnatmake -gnatyM79 hello.adb  # Max line length 79

Why Ada for Hello World?

Even in this simple example, you see Ada’s philosophy:

  1. Explicit Imports - No hidden dependencies
  2. Clear Structure - Procedure declaration, begin/end blocks
  3. Readable Syntax - English-like keywords
  4. Name Redundancy - end Hello; instead of just end
  5. Package Qualification - Ada.Text_IO.Put_Line shows exactly where functions come from

These features become invaluable in large, safety-critical systems where Ada shines.

A Bit of History

Ada was named after Ada Lovelace (1815-1852), who wrote the first computer program - an algorithm for Charles Babbage’s Analytical Engine in 1843, nearly a century before electronic computers existed.

When the U.S. Department of Defense mandated Ada for most projects in 1987, Hello World programs compiled on room-sized VAX computers running VMS. Today, the same code compiles on a Raspberry Pi with GNAT - demonstrating Ada’s remarkable portability across four decades.

Performance Note

Ada’s strong typing and safety features might suggest overhead, but GNAT’s GCC backend produces highly optimized machine code. Ada programs run at C/C++ speeds - the safety checks happen at compile time, not runtime (except for explicit checks like array bounds, which can be disabled for production builds with -gnatp).

Next Steps

Continue to Variables and Data Types to learn about Ada’s powerful type system - the foundation of its safety guarantees.

Key Takeaways

  1. Ada requires explicit imports via with clauses
  2. Programs are procedures with is/begin/end structure
  3. Semicolons terminate statements, not separate them
  4. End clauses include names for error checking
  5. GNAT is part of GCC and widely available
  6. .adb files contain program bodies
  7. Ada is case-insensitive but style guides recommend conventions

Running Today

All examples can be run using Docker:

docker pull codearchaeology/ada:latest
Last updated: