Hello world in Ada

My favorite language is F#, but Ada is also interesting.

mkdir src
Put in this source directory the ada "main" file , hello.adb :
Code:
with Text_IO; 
use Text_IO;
procedure hello is
begin
   Put_Line("Hello world!");
end hello;

Create a gpr file , syntax is not always obvious, hello_world.gpr :
Code:
project Hello_World is
   for Source_Dirs use ("src");
   for Object_Dir use "obj";
   for Exec_Dir use "bin";
   for Main use ("hello.adb");
   package Compiler is
       for Switches ("Ada") use ("-gnat2022", "-O2","-B","-d","-g");
   end Compiler;
   package Builder is
       for Executable ("hello.adb") use "hello.exe";
   end Builder;
end Hello_World;

Execute the following script , compile_run :
Code:
export CC=clang20
export CXX=clang20++
export GCC=clang20
rm -vfR ./obj/*
rm -vfR ./bin/*
gnatmake -vl -we -P hello_world.gpr
./bin/hello.exe
 
How to do object orientation in ada :

In src directory put , counter_pkg.ads :
Code:
-- counter_pkg.ads
package Counter_Pkg is
   type Counter is private;
   function Counter_Make (X: Integer) return Counter;
   procedure Increment(C : in out Counter);
   function Get_Value(C : Counter) return Integer;

private

   type Counter is record
      Value : Integer := 0;
   end record;

end Counter_Pkg;

counter_pkg.adb :
Code:
-- counter_pkg.adb
package body Counter_Pkg is

   function Counter_Make (X: Integer) return Counter is
   c : Counter;
   begin
       c.Value:=X;
       return c;
   end Counter_Make;

   procedure Increment(C : in out Counter) is
   begin
      C.Value := C.Value + 1;
   end Increment;

   function Get_Value(C : Counter) return Integer is
   begin
      return C.Value;
   end Get_Value;

end Counter_Pkg;

hello.adb :
Code:
with Text_IO;
use  Text_IO;
with Counter_Pkg;
use  Counter_Pkg;

procedure hello is

   c : Counter := Counter_Make(5);
   i : Integer := 0;

begin
   Put_Line("Hello world!");
   c.Increment;
   c.Increment;
   i:=c.Get_Value;
   Put_Line("The value is: " & Integer'Image(i));
end hello;

Change the .gpr file and add "gnatX" flag,
Code:
package Compiler is
       for Switches ("Ada") use ("-gnat2022", "-O2","-B","-d","-g","-gnatX");
   end Compiler;
 
Sidenote, some interesting compiler switches to put in the .gpr file,
Code:
package Compiler is
       for Switches ("Ada") use ("-gnat2022", "-O2","-B","-d","-gnatX","-Og","-ffunction-sections","-fdata-sections","-g","-gnatwa","-gnatw.X","-gnatVa","-gnatW8");
end Compiler;
 
And now something different, "manual" memory management in ada.
Ok, memory is safe as opposed to C & C++,

Code:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Unchecked_Deallocation;

procedure Heap_Example is

   type Integer_Ptr is access Integer;
   Ptr : Integer_Ptr;
   procedure Free is new Ada.Unchecked_Deallocation(Integer, Integer_Ptr);

begin

   Ptr := new Integer'(42);
   Put_Line("Value on heap:" & Integer'Image(Ptr.all));
   Free(Ptr);

end Heap_Example;
 
I quite like Ada and have a few legacy projects at work that use(d) it. We decided to migrate them to C++/sys rather than SPARK.

Ada is one of the few languages that supports RAII. Binding Ada to system libraries can leverage that but it isn't common knowledge so memory safety issues typically creep in here (almost exclusively). It would make a cracking interview question.

gnatmake never sat well with me. For one, it ties you down to a single vendor of Ada (Greenhills, DDC, PTC are common vendors outside of AdaCore/GNAT). But just like rustc, the compiler itself has a naive approach to incremental builds so standard Makefiles are complex to write.
 
Some data-types, record, array, vector, double-linked-list,

Code:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
with Ada.Containers.Doubly_Linked_Lists;

procedure Example is

type Person is record
   Name : String (1 .. 10);
   Age  : Integer;
end record;
John : Person := (Name => "John Smith", Age => 30);

type My_Index is range 1 .. 5;
type My_Int_Array is array (My_Index) of Integer;
Arr : My_Int_Array := [10, 20, 30, 40, 50];

package Integer_Vectors is new Ada.Containers.Vectors
    (Index_Type   => Natural,Element_Type => Integer); use Integer_Vectors;
My_Vector : Vector;

package Integer_Lists is new Ada.Containers.Doubly_Linked_Lists (Integer); use Integer_Lists;
My_List : List;

Pos : Integer_lists.Cursor;

begin

    John.Age := John.Age + 1;
    Put_Line("John has age : " & John.Age'Image);

    for I in Arr'Range loop
        Put_Line("Element at index" & I'Image & " is" & Arr(I)'Image);
    end loop;

    My_Vector.Append(10);
    My_Vector.Append(20);
    My_Vector.Append(30);
    Put_Line("First element: " & My_Vector.Element(0)'Image);
    Put_Line("Vector length: " & My_Vector.Length'Image);
    for Item of My_Vector loop
        Put_Line("Value: " & Item'Image);
    end loop;

    My_List.Append (10);
    My_List.Append (20);
    My_List.Prepend (5);
    Put_Line ("Forward Traversal:");
    for Cursor in My_List.Iterate loop
        Put_Line (Integer'Image (Element (Cursor)));
    end loop;
   
    Put_Line ("Backward Traversal:");
    Pos := My_list.Last;
    while Has_Element (Pos) loop
        Put_Line (Integer'Image (Element (Pos)));
        Pos := Previous (Pos);
    end loop;

end Example;
 
Or-type or Union-type in ada,

Code:
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Text_IO.Unbounded_IO; use Ada.Text_IO.Unbounded_IO;

procedure Example is
   type Data_Kind is (Is_Int, Is_String);
   type My_Union(Kind : Data_Kind:= Is_Int) is record
      case Kind is
         when Is_Int =>
            Int_Value : Integer;
         when Is_String =>
            Str_Value : Unbounded_String;
      end case;
   end record;
   Val1 : My_Union;
   Val2 : My_Union;

procedure Display_Val (x : My_Union) is
    begin
        case x.Kind is
            when Is_int =>
                Put (x.Int_Value);
                New_Line;
            when Is_String =>
                Put (x.Str_Value);
                New_Line;
        end case;
end Display_Val;

begin
   Val1 := (Kind => Is_Int, Int_Value => 42);
   Val2 := (Kind => Is_String, Str_Value => To_Unbounded_String("Hello Ada"));
   Display_Val(Val1);
   Display_Val(Val2);
end Example;
 
Things which don't work on FreeBSD, formatting code , connecting to postgresql, using a GUI toolkit. Later i will take the liberty to post some linux only stuff here. [Not my habit]
 
Fyi,

FreeBSD's support for Ada is considered poor primarily due to a lack of dedicated maintenance for its compiler toolchain and historical shifts in its "Ports" infrastructure. As of 2025, while Ada is available, it remains a challenge to use compared to more popular languages or operating systems.

Key Reasons for Poor Support

  • Deprecation of Key Ports: For years, FreeBSD relied on the gcc-aux port for Ada support. This port was deprecated and officially expired in early 2022, leaving the operating system without an officially maintained Ada compiler for a period.
  • Maintenance Gaps: Ada support on FreeBSD historically relied on individual contributors, such as John Marino, who maintained ports for both FreeBSD and DragonFly BSD. When such key maintainers step away, the "niche" nature of both the language and the OS makes it difficult to find replacements, leading to "bit rot" and subtle bugs.
  • Toolchain Complexity: Building a modern Ada compiler (GNAT) on FreeBSD requires significant effort. Users often have to use "hacks" or unofficial repositories to get GCC 12+ or 14 with Ada support working, as the standard FreeBSD GCC ports sometimes exclude Ada to avoid conflicts or simplify builds.
  • Binary Availability: While Linux distributions often provide ready-to-use Ada binaries, FreeBSD users frequently have to compile the entire toolchain from source or use unofficial poudriere repositories, which is time-consuming and error-prone.
 
Back
Top