Ada — Reference
Source: https://ada-lang.io/
Ada
- Created: 1980 (ANSI MIL-STD-1815), refined as Ada 83 (1983). Designed by a team led by Jean Ichbiah at Honeywell/Bull under a US Department of Defense contract (1977-1983). Named after Ada Lovelace.
- Latest stable: Ada 2022 = ISO/IEC 8652:2023 (approved May 2023). Predecessors: Ada 83, Ada 95 (first ISO-standardized OO language), Ada 2005, Ada 2012.
- Paradigms: Imperative, procedural, modular, object-oriented (since 95), generic, concurrent (built into the language since 83), contract-based (since 2012).
- Typing: Strong, static, nominal (named types are distinct even with same structure), with first-class subtypes (range constraints) and discriminated record types. Stricter than nearly any mainstream language.
- Memory: No GC by default. Stack-allocated by default; heap via
newreturning access types (typed pointers). Storage pools + controlled types (Initialize/Finalize/Adjust) give RAII. SPARK subset disallows heap entirely; Ada with controlled types is deterministic. - Compilation: Ahead-of-time native code. Major compilers: GNAT (GCC frontend, FOSS, the dominant implementation — both GCC mainline and AdaCore-maintained GNAT Pro / GNAT Community), GNAT-LLVM (Ada to LLVM IR), PTC ObjectAda (commercial), Green Hills AdaMULTI.
- Primary domains: Aerospace (Boeing 777 fly-by-wire, Airbus A350, F-35), defense, rail signaling (SNCF, NYC subway CBTC), air traffic control, medical devices, automotive (ISO 26262), spacecraft (Mars rovers’ control SW partially), increasingly embedded + Rust-adjacent safety work.
- Official docs: https://ada-lang.io/ (community + Alire), https://www.adacore.com/about-ada (AdaCore), https://www.ada-auth.org/standards/ada22.html (ARM 2022 — Ada Reference Manual).
At a glance
Ada is what you get when a language is designed by a committee whose explicit goal is “find errors at compile time, never at runtime, especially not on a Boeing.” Concurrency, contracts, generics, and strong typing are all in the base language, not bolted on. The 2022 revision modernizes syntax (parallel reductions, delta aggregates, declare expressions) without losing the safety guarantees. SPARK 2014+ is a verifiable subset where you can prove with a theorem prover that there is no runtime error and that contracts hold for all inputs — used in production at Airbus, Thales, NVIDIA’s secure firmware, and AWS’s Nitro hypervisor work.
Getting started
Install via Alire (the modern Ada package manager and toolchain installer, like Rust’s rustup+cargo):
- Download Alire 2.x from https://alire.ada.dev/.
alr toolchain --select— picksgnat_nativeandgprbuildversions.- Verify:
gnatmake --version(compiler driver) andgprbuild --version(project builder).
Alternative: sudo apt install gnat gprbuild (Debian/Ubuntu, GCC’s GNAT — slightly older), brew install gnat, MSYS2 on Windows.
Hello world (hello.adb):
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
begin
Put_Line ("Hello, world!");
end Hello;With Alire: alr init --bin hello && cd hello && alr run. Without: gnatmake hello.adb && ./hello.
Project layout with Alire:
hello/
alire.toml
hello.gpr -- GPRBuild project file
src/
hello.adb -- main
my_pkg.ads -- package spec
my_pkg.adb -- package body
obj/ -- generated
bin/ -- generated
.ads = package spec (interface, like a .h), .adb = package body (implementation, like a .c). The split is mandatory for any reusable code.
Package/build tool. GPRBuild is the standard build system; .gpr files describe sources, build modes, switches. Alire wraps GPRBuild and adds a Cargo-like dependency manager pulling from the Alire Community Index (alr search, alr with my_dep).
REPL. None. Edit-compile-run. AdaCore’s GNAT Studio IDE provides interactive debug.
Basics
Types & literals. Built-in: Integer, Long_Integer, Long_Long_Integer, Float, Long_Float, Character, Wide_Character, Wide_Wide_Character (32-bit Unicode), String, Wide_String, Boolean. Numeric literals: 123, 16#FF# (hex), 2#1010# (binary), 1_000_000 (digit grouping), 3.14E-2. Crucially, you usually define your own numeric types to enforce semantic meaning:
type Meters is new Float; -- distinct from plain Float
type Kelvin is new Float range 0.0 .. Float'Last; -- range-constrained
subtype Positive_Meters is Meters range 0.0 .. Meters'Last;A Meters cannot be silently mixed with a plain Float or with Kelvin — you’d need an explicit conversion.
Variables/scoping. Declared in a declare ... begin ... end block or in a procedure/function/package declarative part. Constants: Pi : constant Float := 3.14;. Fully lexical scope; with + use to import packages. use type Meters imports just operators.
Control flow. if ... then ... elsif ... else ... end if, case X is when 1 => ...; when 2 | 3 => ...; when others => ...; end case; (must be exhaustive on enumerations and ranges). Loops: loop ... end loop (basic), while Cond loop ... end loop, for I in 1 .. N loop ... end loop, for I in reverse 1 .. N loop, for E of Container loop (since 2012). exit when Cond; for early break. Declare expressions (Ada 2022): (declare X : constant := F (Y); begin X * X).
Procedures & functions. procedure P (A : Integer; B : in out String); and function F (X : Integer) return Integer;. Parameter modes: in (default, read-only), out (write-only), in out. Named parameters at call site: P (A => 1, B => Buf). Default values: procedure P (A : Integer := 0). Expression functions (since 2012): function Square (X : Integer) return Integer is (X * X);.
Strings. String is a fixed-length array of Character. S : String := "hello"; length is locked at declaration. For varying-length, use Ada.Strings.Unbounded.Unbounded_String or Ada.Strings.Bounded. Concatenation: &. Slicing: S (2 .. 4). Unicode strings via Wide_Wide_String (UTF-32 internally) and Ada.Strings.UTF_Encoding.
Collections. Native arrays: type Vector is array (1 .. 100) of Float;. Arrays carry their own bounds — pass them around, query A'First, A'Last, A'Length. Standard containers (Ada.Containers.Vectors, .Hashed_Maps, .Ordered_Sets, .Doubly_Linked_Lists, etc.) since 2005. Aggregates (literals): (1 => 10, 2 => 20, others => 0). Delta aggregates (Ada 2022): (A with delta Field => 5).
Intermediate
Type system depth. Subtypes (subtype Small is Integer range 1 .. 10) constrain values; runtime range checks on assignment. Discriminated records are tagged unions:
type Shape (Kind : Shape_Kind) is record
case Kind is
when Circle => Radius : Float;
when Square => Side : Float;
end case;
end record;Tagged types (Ada 95+) provide single inheritance + dispatching: type Animal is tagged record ... end record; with primitive operations dispatched via Object'Class. Interfaces (Ada 2005) provide multiple-inheritance-of-interface. Access types are typed pointers with named pool: type Int_Ptr is access Integer; X : Int_Ptr := new Integer'(42);. General access types (access all T) can point to stack data with 'Access attribute (compiler enforces lifetime via accessibility checks).
Modules. Packages are the unit of modularity. Spec (.ads) lists what’s exported; body (.adb) provides implementations. Private types hide implementation:
package Stacks is
type Stack is private;
procedure Push (S : in out Stack; X : Integer);
private
type Stack is record ... end record;
end Stacks;Child packages (Parent.Child) extend without recompiling parent. Generic packages parameterize over types/values/packages — Ada had this in 1983.
Error handling. Exceptions: raise Constraint_Error;. Predefined: Constraint_Error, Program_Error, Storage_Error, Tasking_Error. User-defined: Bad_Input : exception;. Handle with:
begin
...
exception
when Bad_Input => Put_Line ("oops");
when E : others => Put_Line (Exception_Information (E));
end;Contracts (Ada 2012): Pre, Post, Type_Invariant, Predicate aspects. Compiler can elide checks if SPARK proves them.
Concurrency primitives. Three layers, all in the standard:
- Tasks:
task type Worker is entry Start (X : Integer); end Worker;— first-class concurrent units. Schedulable independently; suspend/resume viaentryrendezvous. - Protected objects: monitor-style, lock-free interface.
protected Counter is procedure Inc; function Get return Integer; private Value : Integer := 0; end Counter;—procedurewrites (exclusive lock),functionreads (shared lock),entryblocks on a barrier condition. - Rendezvous: synchronous task-to-task communication via
entry/accept.
I/O. Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO, Ada.Sequential_IO, Ada.Direct_IO, Ada.Streams.Stream_IO. Generic IO instantiated per type: package Int_IO is new Ada.Integer_Text_IO (Integer);. Ada.Command_Line for argv. Network/file/process: Ada.Directories, GNAT.Sockets, AWS (Ada Web Server) for HTTP.
Stdlib highlights. Ada.Containers (vectors, hashed maps, ordered maps, sets, lists, queues), Ada.Calendar + Ada.Real_Time (the latter is monotonic, for hard real-time), Ada.Numerics (math, generic complex, generic random), Ada.Strings.* (fixed/bounded/unbounded), Ada.Streams (binary streaming protocol), Ada.Task_Identification, Ada.Synchronous_Task_Control. Ada 2022 added parallel reduction in the language and 'Reduce attribute: Sum := Arr'Reduce ("+", 0).
Advanced
Memory model. Storage allocation is per-access-type’s storage pool. Default pool is the heap, but you can define a custom pool (a Root_Storage_Pool derivative) — useful for region allocators, pool-per-task, or no-allocation embedded targets. Unchecked_Deallocation exists but you have to instantiate it explicitly with the access type — Ada makes you announce that you’re doing something dangerous. Controlled types (Ada.Finalization.Controlled) provide constructor (Initialize), copy hook (Adjust), and destructor (Finalize) — true RAII.
Concurrency deep dive. Tasks are scheduled by the runtime; on most targets they map 1:1 to OS threads, but the runtime is pluggable. Ravenscar profile (pragma Profile (Ravenscar);) restricts tasking to a deterministic subset proven analyzable for hard real-time / safety-critical (no dynamic task creation, single entry per protected object, etc.). Jorvik profile (Ada 2022) is a relaxed Ravenscar suitable for multicore. Atomic objects via pragma Atomic/pragma Volatile. Synchronous Suspension Objects for low-level signaling. Ada 2022 adds parallel blocks and parallel loops: parallel for I in 1 .. N loop ... end loop; — the runtime parallelizes safely (no shared mutable state allowed without sync).
FFI. pragma Import (Convention, Ada_Name, "External_Name") and pragma Export. Conventions: C, Cpp, Fortran, Ada, Stdcall, Asm. Interfaces.C package gives int, char, size_t, Strings.chars_ptr, etc. Example:
function Strlen (S : Interfaces.C.Strings.chars_ptr) return Interfaces.C.size_t
with Import => True, Convention => C, External_Name => "strlen";Aspects (Ada 2012 syntax) above are equivalent to the older pragma Import syntax. Representation clauses specify exact memory layout — bit-level — for hardware register mapping:
type Status is record
Ready : Boolean;
Error : Boolean;
Code : Integer range 0 .. 63;
end record;
for Status use record
Ready at 0 range 0 .. 0;
Error at 0 range 1 .. 1;
Code at 0 range 2 .. 7;
end record;
for Status'Size use 8;Reflection. Limited. 'Image (since 2022, generalized to all types) gives a string repr; 'Value parses back. Ada.Tags lets you query and dispatch on a tagged type’s tag. No general-purpose runtime metaclass system — Ada deliberately resolves at compile time.
Performance tools. GNATprove for SPARK proof obligations. gnatcoverage for MC/DC coverage. gnattest for AUnit-style unit tests. GNATstack for worst-case stack analysis. GNATdashboard integrates SonarQube. Compile flags: -O2 -gnatn (inline), -gnatE (dynamic elaboration checks), -gnata (enable assertions/contracts at runtime). Standard Linux profilers (perf, callgrind) work on GNAT-compiled native code.
God mode
SPARK + GNATprove. SPARK is an Ada subset (no exceptions, no allocators outside specified pools, no aliasing) plus contracts. GNATprove uses Why3 + Z3/CVC4/AltErgo to prove at compile time that no Constraint_Error/overflow/division-by-zero can occur and that all Pre/Post contracts hold for all inputs. Used in NVIDIA’s secure boot, Boeing 787 brake/landing systems, the AdaCore Tokeneer reference implementation. Gold-rush level: levels are Stone (legality), Bronze (data initialization), Silver (no runtime errors), Gold (functional correctness via contracts), Platinum (full formal proof of security-relevant properties).
Generic packages. The original “modules with parameters.” generic type Element is private; type Index is range <>; package Stacks is .... Instantiate: package Int_Stack is new Stacks (Element => Integer, Index => 1 .. 100);. Generic formal types can be private, (<>) (any), range <> (signed integer), digits <> (float), mod <>, tagged private, access, even formal subprograms and formal packages. C++ templates and Java generics are pale imitations.
Tasks + protected objects + entries. A protected entry is a guard + body: entry Get (X : out Integer) when Available is begin X := Buffer; Available := False; end Get;. Callers block until Available becomes True, then enter atomically. This is a high-level synchronization primitive that compiles to efficient lock+condvar code; it’s how you implement bounded buffers, semaphores, signals, etc., safely.
Representation clauses + bit-level layout. Map a record onto a memory-mapped device register exactly:
UART_Status : Status with Address => System'To_Address (16#4000_0010#);The compiler emits the bit-twiddling for you. Combined with pragma Atomic and pragma Volatile, this is how Ada drives bare-metal embedded.
Aspects vs pragmas. Modern Ada (2012+) prefers aspects (declaration-level annotations after with) over pragmas. procedure F (X : Integer) with Pre => X > 0, Post => F'Result = X * 2;. pragma Restrictions constrains the entire program (pragma Restrictions (No_Allocators); bans new everywhere — useful for proving stack-only code). pragma Profile (Ravenscar) enables a whole bundle of restrictions for safety-critical work.
Ada 2022 highlights. Parallel reduction ('Reduce), parallel loops (parallel for), declare expressions, delta aggregates ((A with delta Field => 5)), big numbers (Ada.Numerics.Big_Numbers), generalized 'Image, generalized iterator filters, for X of A when Cond. Largest practical wins: declare expressions (no more let-block boilerplate) and delta aggregates (functional update without writing every field).
GNAT internals. GNAT is a GCC frontend: parses + semantic-checks Ada into GNU’s GENERIC tree, then GCC backends do the rest. The frontend is written in Ada and is itself open source — readable when you need to understand a corner case. GNAT-LLVM alternative emits LLVM IR, useful for non-GCC targets. gnatmake drives the dependency-checked recompile; gprbuild generalizes it for multi-language projects.
Idioms & style
- Naming:
Title_Case_With_Underscoresfor everything (types, packages, procedures, parameters, variables, constants).lower_caseis non-idiomatic. Ada is case-insensitive but the convention is firm. Pre/Postcontracts on every public subprogram. Even if you don’t run SPARK, runtime checks via-gnatacatch bugs in test.- Define new numeric types (
type Meters is new Float) instead of usingFloateverywhere — Ada’s strong-typing payoff vanishes if you don’t. - Use named parameter associations at call sites — readability + safety against argument reordering.
usesparingly. Preferwith My_Pkg;thenMy_Pkg.Foo (...).useit only inside the procedure that needs it (use My_Pkg;insidedeclare).use type T;brings just operators of a type into scope — a good middle ground.- Prefer
for X of A loopover indexedfor I in A'Range loopwhen you don’t need the index. - Style guide: GNAT Coding Style is the de-facto reference, enforced by
gnatpp(auto-pretty-print) andgnatcheck. Run them in CI. - Expert review focus: (1) Are unconstrained access types necessary? Most code can be heap-free. (2) Are tasking primitives (
task/protected/entry) used instead of busy-loops or shared booleans? (3) Are contracts present and meaningful, or justTrue => True? (4) Are subtype range constraints used to communicate intent? (5) Are exceptions used for exceptional cases only, not control flow? (6)pragma Restrictionsset appropriately for embedded/safety-critical targets? (7) IsUnchecked_*(Unchecked_Conversion,Unchecked_Deallocation,Unchecked_Access) justified and minimized?
Ecosystem
- Compilers: GNAT Pro (commercial, AdaCore), GNAT Community (free, AdaCore), GCC GNAT (mainline), GNAT-LLVM, PTC ObjectAda, Green Hills AdaMULTI.
- Package manager / build: Alire (
alr) — modern Cargo-equivalent. GPRBuild for direct project files. Alire Community Index: https://alire.ada.dev/crates. - Web: AWS (Ada Web Server, GNATCOM), GNOGA (web UI from Ada). Embedded web: SPARK-friendly stacks for IoT.
- GUI: GtkAda (GTK bindings), QtAda (Qt bindings).
- Embedded / RTOS: Ravenscar runtime on ARM Cortex-M (STM32, etc.), PolyORB-HI for distributed embedded, VxWorks 653 (ARINC 653, certified avionics).
- Formal verification: SPARK Pro + GNATprove, AdaControl (custom rule checker).
- Testing: AUnit (xUnit-style), gnattest (auto-generates harness), gnatcoverage (statement/decision/MC/DC).
- Notable users: Boeing 777/787, Airbus A350/A380, Lockheed Martin F-35, NASA (parts of various missions), SNCF and DB rail signaling, NYC subway CBTC, Eurocontrol ATC, Thales, Saab, Bombardier, NVIDIA (security firmware), AdaCore (compiler infrastructure itself in Ada).
Gotchas
useclauses can hide ambiguity. Two packages both exportingPut_Linebecomes a compile error afteruse. Prefix withPkg.or useuse typeselectively.- Default
Floatprecision is implementation-defined. UseLong_Floator define your owndigits 15type for portable double-precision. Stringlength is locked at declaration.S : String := Read_Line;won’t compile because the length isn’t known. UseUnbounded_Stringordeclare S : String := Read_Line; begin ...with an initializer.- Tasking on bare metal needs the right runtime. GNAT ships
zfp(zero-footprint, no tasking, no exceptions),light-tasking,embedded(Ravenscar), andfullruntimes. Mix-up means link errors or missing primitives. - Generics are not first-class. They must be instantiated before use — you can’t pass a generic package as a value.
genericdeclarations are templates compiled at instantiation site. - Accessibility checks.
'Accesson a local variable returns an access type whose accessibility is the local frame; assigning it to a longer-lived access type is a compile error. Use'Unchecked_Accessonly when you can prove the lifetime — otherwise dangling pointer. - Elaboration order. Package bodies are elaborated at program startup in dependency order; circular elaboration is a
Program_Errorat runtime. Usepragma Elaborate_Allor restructure to avoid surprises. - Mixed compiler
.alifiles. GNAT’s.ali(Ada Library Information) files are not portable between GNAT versions. Rebuild from source when switching toolchains. - SPARK doesn’t permit allocators (in pre-2022 modes). A SPARK-able program must be heap-free or use restricted ownership semantics introduced in SPARK 2022. Don’t promise SPARK before checking the subset.
Constraint_Errorvs-gnato. Integer overflow raisesConstraint_Erroronly with-gnato(orpragma Overflow_Mode (CHECKED, CHECKED)); default isMINIMIZED(use widest representation). Different default behavior per build target — pin it.
Citations
- Ada Reference Manual 2022 (ISO/IEC 8652:2023): https://www.ada-auth.org/standards/ada22.html.
- ada-lang.io (community + Alire): https://ada-lang.io/.
- AdaCore: https://www.adacore.com/about-ada.
- Wikipedia, Ada (history, standards, users): https://en.wikipedia.org/wiki/Ada_(programming_language).
- Alire (Ada package manager): https://alire.ada.dev/.
- GNAT (GCC docs): https://gcc.gnu.org/onlinedocs/gnat_rm/.
- SPARK 2014 docs: https://docs.adacore.com/spark2014-docs/html/ug/.
- GNAT Coding Style: https://gcc.gnu.org/onlinedocs/gnat-style/.
- Ravenscar profile (ARM with
pragma Profile (Ravenscar);): https://www.adacore.com/gnatpro-safety-critical/profiles/ravenscar. - learn.adacore.com (tutorials): https://learn.adacore.com/.