
Ada was designed in the late 1970s, first released in 1983, and was the first object-oriented language to be internationally standardized (in 1995). The language was designed to facilitate the design of large, long-lived, efficient, reliable software systems. It is particularly useful in concurrent, distributed, real-time, and embedded systems. It is an extremely safe language and is thus primarily used today in avionics, air traffic control, rail systems, embedded systems, medical devices, and communication.
As a general purpose language, you can use it in virtually every interesting area of Computer Science.
Some resources you’ll want to know about are:
The language was designed for complex tasks. Readability was an important design goal, so many people find it verbose. But others consider this is a good thing. Getting work done in Ada is a very pleasant experience because many aspects of good software engineering are enforced by the language.
The language has evolved over time, with the following major versions:
| Version | Wikibook | Reference Manual | Rationale or Overview | Standard |
|---|---|---|---|---|
| Ada 83 | Info | RM '83 | ISO/IEC 8652:1987 | |
| Ada 95 | Info | ARM 95 AARM 95 |
Rat 95 | ISO/IEC 8652:1995(E) with COR.1:2000 |
| Ada 2005 | Info | ARM 2005 AARM 2005 |
Rat 2005 | ISO/IEC 8652:1995(E) with COR.1:2001 and AMD.1:2007 |
| Ada 2012 | Info | ARM 2012 AARM 2012 |
Rat 2012 | ISO/IEC 8652:2012(E) |
| Ada 2022 | Info | ARM 2022 AARM 2022 |
Overview | ISO/IEC 8652:2023 |
The notes on this page are designed to only get you started and cover only the most basic elements of the language. I have notes on Ada concurrency elsewhere, but even these cover only a small subset of the language.
For a more modern and much better tutorial, see the Wikibook or learn.adacore.com.
The simplest kind of program is a simple procedure with no parameters:
with Ada.Text_IO;
procedure Hello is
begin
Ada.Text_IO.Put_Line ("Hello, world");
end Hello;
The modern way to execute Ada programs is to use the package manager Alire. If you are familiar with Rust, Alire is equivalent to Cargo. If you set Alire up correctly, and ask it to build a binary crate for your hello program, you should be able to run it with:
$ alr run ⓘ Building ada=0.0.1/ada.gpr... Compile [Ada] hello.adb Bind [gprbind] hello.bexch [Ada] hello.ali Link [link] hello.adb ✓ Build finished successfully in 0.13 seconds. Hello, world
Since the run command produces a lot of output, you can just do alr build, which puts the executable in the bin folder. Then just run:
$ bin/hello Hello, world
Here’s a program that prints some lucky numbers:
-------------------------------------------------------------------------------
-- This program writes out a table of all Pythagorean triples whose elements
-- are in the range 1..100. Each of the triples are written on a single line
-- of text with each value right-justified in a text field six characters
-- wide.
-------------------------------------------------------------------------------
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Triple is
begin
Put_Line (" A B C");
Put_Line ("------------------");
for C in 1 .. 100 loop
for B in 1 .. C loop
for A in 1 .. B loop
if A ** 2 + B ** 2 = C ** 2 then
Put (A, Width => 6);
Put (B, Width => 6);
Put (C, Width => 6);
New_Line;
end if;
end loop;
end loop;
end loop;
end Triple;
And more numbers you know:
-------------------------------------------------------------------------------
-- This program prints out Fibonacci numbers in octal, decimal and hexadecimal
-- in reverse order. The number of Fibonacci numbers that the user wants
-- to see is input on the command line, but if no arguments are given on the
-- command line then the program will prompt the user to input that value.
--
-- This script is meant only to demonstrate a few Ada features, such as user-
-- defined exceptions, enumeration types, the 'Pos attribute, attributes, the
-- reverse keyword, the case statement, and assignment to array slices.
-------------------------------------------------------------------------------
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Command_Line;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Command_Line;
procedure Fib is
Number_Of_Values_Requested : Integer;
-- In Ada there are a number of predefined exceptions, but we can declare
-- our own. Too_Few will be raised if the user does not ask for at least
-- two Fibonacci numbers, and Bad_Argument will be raised if the program
-- can not make sense out of the command line argument:
Too_Few, Bad_Argument : exception;
type Number_List is array (Integer range <>) of Integer;
type Number_System is (Octal, Decimal, Hex);
-- The function Get_User_Input returns the user's "input" as follows:
-- First it checks to see if any argument was entered on the command
-- line and if so attempts to convert that value to a natural number.
-- If the command line has no parameters the user is prompted for an
-- input. In either case, the value input must be greater than or
-- equal to 2; the function raises Too_Few if it is not.
function Get_User_Input return Integer is
Size : Integer;
begin
if Argument_Count > 0 then
Size := Natural'Value (Argument (1));
else
Put ("How many numbers do you want (minimum = 2)? ");
Get (Size);
end if;
if Size < 2 then
raise Too_Few;
end if;
return Size;
exception
-- Natural'Value raises a Constraint_Error if the string cannot be
-- converted to a natural. Let's repackage this to a nicer exception
when Constraint_Error => raise Bad_Argument;
end Get_User_Input;
-- Compute (Size) finds and displays the first Size Fibonacci numbers.
-- It first allocates an array of just the right size to store the
-- numbers, then loads the array by performing the straightforward
-- calculations. Then the array is printed backwards three times
-- with different bases, using the helper function Display.
procedure Compute (Size : Integer) is
Fibs : Number_List (1 .. Size);
procedure Display (B : Number_System) is
begin
Put ("Fibonaccis in " & B'Image & " backwards: ");
New_Line (2);
for I in reverse Fibs'Range loop
case B is
-- The order of arguments should be value, width, base. But
-- you can use argument association to change things up a bit.
when Decimal => Put (Fibs (I), 15, 10);
when Octal => Put (Fibs (I), Base => 8, Width => 15);
when Hex => Put (Base => 16, Item => Fibs (I), Width => 15);
end case;
New_Line;
end loop;
New_Line (2);
end Display;
begin
Fibs (1 .. 2) := [1, 1];
for I in 3 .. Size loop
Fibs (I) := Fibs (I - 1) + Fibs (I - 2);
end loop;
Display (Octal);
Display (Decimal);
Display (Hex);
end Compute;
begin
Number_Of_Values_Requested := Get_User_Input;
Compute (Size => Number_Of_Values_Requested);
Put_Line ("Program successfully completed");
exception
when Too_Few => Put_Line ("Not enough values requested");
when Bad_Argument => Put_Line ("Illegal command line argument");
when Constraint_Error => Put_Line ("I can't find that many");
when Data_Error => Put_Line ("That's not a decent response");
end Fib;
And prime numbers, because you may have expected to see them:
------------------------------------------------------------------------------
-- This program computes and prints all the prime numbers up to and including
-- a number which is provided as the sole argument on the command line, or,
-- if no command line arguments are given, input by the user in response to
-- a prompt, and then reports how many primes it found.
------------------------------------------------------------------------------
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Command_Line;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada;
procedure Prime_Sieve is
Limit : Natural; -- will find primes up to this
How_Many : Natural; -- how many primes were found
procedure Compute_Primes (Up_To : Natural; Count : out Natural) is
Sieve : array (2 .. Up_To) of Boolean := (others => True);
begin
Count := 0;
for I in Sieve'Range loop
if Sieve (I) then
Put (Item => I, Width => 12, Base => 10);
Count := Count + 1;
declare -- start a block
K : Natural := I; -- variable local to the block
begin
while K <= Sieve'Last - I loop
K := K + I; -- go to next multiple
Sieve (K) := False; -- check off this multiple
end loop;
end; -- after the block K does not exist
if Count mod 6 = 0 then -- write six primes per row
New_Line;
end if;
end if;
end loop;
New_Line (2);
end Compute_Primes;
begin
if Command_Line.Argument_Count = 0 then
-- If no command line argument, prompt the user for input
Put ("Find primes up to what number? ");
Get (Limit);
else
Limit := Natural'Value (Command_Line.Argument (1));
end if;
Compute_Primes (Up_To => Limit, Count => How_Many);
Put_Line ("I found" & How_Many'Image & " primes.");
exception
when Constraint_Error => Put_Line ("Illegal command line argument");
when Data_Error => Put_Line ("I don't understand that input");
end Prime_Sieve;
The Ada language is comprised of two parts:
Required of all implementations.
ARM Clauses 1-13, and Annexes A, B, and J.
Not required of an implementation.
ARM Annexes C, D, E, F, G, and H.
You should browse the reference manual table of contents now to see what these clauses and annexes refer to.
Ada is clearly not a small language.
abortabsabstractacceptaccessaliasedallandarrayatbeginbodycaseconstantdeclaredelaydeltadigitsdoelseelsifendentryexceptionexitforfunctiongenericgotoifininterfaceislimitedloopmodnewnotnulloforothersoutoverridingpackageparallelpragmaprivateprocedureprotectedraiserangerecordremrenamesrequeuereturnreverseselectseparatesomesubtypesynchronizedtaggedtaskterminatethentypeuntilusewhenwhilewithxorAn Ada program is made up of a collection of program units, which can be either subprograms or packages. Ada units are designed to have well specified interfaces so they are highly reusable.
Here is a trivial example of a program made up of a package exporting a type Float_Array and two functions, Mean and Variance:
package Stats is
type Float_Array is array (Integer range <>) of Float;
function Mean (A : Float_Array) return Float;
function Variance (A : Float_Array) return Float;
end Stats;
To show off information hiding features, the package contains a local, non-exported utility function called Sum:
package body Stats is
function Sum (A : Float_Array) return Float is (A'Reduce ("+", 0.0));
function Mean (A : Float_Array) return Float is
(Sum (A) / Float (A'Length));
function Variance (A : Float_Array) return Float is
M : constant Float := Mean (A);
Square_Diffs : Float_Array (A'Range);
begin
Square_Diffs := [for I in A'Range => (A (I) - M) ** 2];
return Mean (Square_Diffs);
end Variance;
end Stats;
Here’s a command line application that shows off the functions:
with Ada.Text_IO, Ada.Float_Text_IO, Stats;
use Ada.Text_IO, Ada.Float_Text_IO, Stats;
procedure Show_Stats is
A : constant Float_Array := [3.0, 5.0, 8.0, 2.0];
begin
Put (Mean (A)); New_Line;
Put (Variance (A)); New_Line;
end Show_Stats;
There was a lot going on there!
See Section 3 of the Reference Manual, particularly the note at the bottom of Section 3.2 which gives a really nice hierarchy of the kinds of types in the type system, and a few of the built-in (standard) types. Here’s a summary of that information:
All Types
│
┌─────────────────┴─────────────────┐
│ │
Elementary Composite
│ │
┌───┴──────┐ ┌─────┴─────────┐
│ │ │ │
Scalar Access Untagged Tagged
│ ├─ To Object ├─ Array ├─ Nonlimited Tagged Record
│ └─ To Subprogram │ └─ String └─ Limited Tagged
│ ├─ Record ├─ Limited Tagged Record
├─ Discrete ├─ Task └─ Synchronized Tagged
│ ├─ Enumeration └─ Protected ├─ Tagged Task
│ │ ├─ Character └─ Tagged Protected
│ │ └─ Boolean
│ └─ Integer
│ ├─ Signed
│ └─ Modular
│
└─ Real
├─ Floating Point
└─ Fixed Point
├─ Ordinary
└─ Decimal
Here’s a program that illustrates a few of the simplest types in use (by simple we mean that it avoids function types, variant records, tagged records, and anything for concurrency):
-------------------------------------------------------------------------------
-- This program doesn't do anything useful; it just illustrates some of Ada's
-- simple types and some interesting aspects of the type system. It is far
-- from complete. So much is missing, but it's a start.
-------------------------------------------------------------------------------
with Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO, Ada.Float_Text_IO;
with Ada.Numerics.Elementary_Functions, Ada.Assertions;
use Ada.Numerics.Elementary_Functions, Ada.Assertions;
procedure Simple_Types is
-- Simple object declarations with some predefined numeric types:
Count : Integer := 0; -- vars MAY have initializers
Pi : constant Float := Arccos (-1.0); -- consts MUST have initializers
Tau : constant Float := 2.0 * Pi; -- can reference previous ones
K : constant Integer := 2 ** 10; -- 1K = 1024
-- Enumeration types. type Boolean = (False, True) is predefined. Here are
-- some we define ourselves:
type Color is (Black, Blue, Green, Cyan, Red, Magenta, Yellow, White);
type Day is (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
-- Subtypes. These are not "real" types, in that objects you declare
-- as having the subtype really "have" the original type. They may lead
-- to runtime checks. The subtypes Natural and Positive of Integer are
-- predefined.
subtype Reddish is Color range Red .. Yellow;
subtype Weekday is Day range Mon .. Fri;
subtype Byte is Integer range 0 .. 255;
-- A subtype that does not restrict a type is just an alias.
subtype Int is Integer;
-- Constrained Array Types. Being able to define an array's index type as
-- an enumeration, rather than being restricted to 0..N-1, sets Ada apart
-- from a lot of other languages.
type Trio is array (1 .. 3) of Byte;
type Color_Chart is array (0 .. K - 1) of Color;
type Time_Card is array (Day) of Float;
type Short_Time_Card is array (Weekday) of Float;
-- Unconstrained array type declarations are just fine. These MAY be
-- constrained in a subtype declaration, but MUST be constrained in an
-- object declaration, since an object declaration allocates memory.
type Matrix is array (Integer range <>, Integer range <>) of Float;
subtype Mat_33 is Matrix (1 .. 3, 1 .. 3);
Grid : Matrix (1 .. 8, 1 .. 8);
Ident : constant Mat_33 := [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]
];
-- Enumeration types may contain identifiers or character literals.
type Roman_Digit is ('I', 'V', 'X', 'L', 'C', 'D', 'M');
type Roman is array (Positive range <>) of Roman_Digit;
-- The following are predefined (i.e. they appear in package Standard):
--
-- subtype Positive is Integer range 1..Integer'Last
-- type String is array (Positive range <>) of Character;
-- Character array subtypes and objects.
Prompt : String (1 .. 50);
Greeting : constant String (1 .. 5) := "Hello";
subtype Line is String (1 .. 80);
C_Comment : constant Line := [1 => '/', 2 .. 79 => '*', 80 => '/'];
Ninety_Eight : constant Roman := "XCVIII";
-- Another enumeration type:
type Month_Name is (
Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
);
-- Record types:
type Date is record
Day : Positive range 1 .. 31;
Month : Month_Name;
Year : Integer;
end record;
type Point is record
X : Float := 0.0; -- fields may have initial values
Y : Float := 0.0;
end record;
-- Record types can have discriminants. An object S of type Square has two
-- fields: S.Side and S.Data. The discriminant Side must be given a value
-- when the object is declared and is read only.
type Square (Side : Integer) is record
Data : Matrix (1 .. Side, 1 .. Side);
end record;
subtype Three_Dimensional_Transform is Square (4);
A_Funny_Thing : Square (2) := (
2,
[[1.0, 3.2], [9.9, 3.88E-28]]
);
-- You can specify a default for the discriminant:
subtype Buffer_Size is Positive range 256 .. 65536;
type Buffer (Size : Buffer_Size := 256) is record
Position : Positive;
Value : String (1 .. Size);
end record;
-- If a value is specified for an object of a record type with a default
-- discriminant, then it is a normal constrained object...
K_Buffer : Buffer (1024); -- 1024 elements
-- ...but if no value is specified, the object is unconstrained, meaning
-- you can actually change the discriminant at run time (provided that
-- assignments are made to all fields simultaneously).
My_Buffer : Buffer; -- 256 elements but changeable
-- Arbitrary linked structures require mutually recursive types. Since
-- Ada elaborates declarations strictly in order, some types must be
-- declared before they are defined.
type Node; -- incomplete type declaration
type Link is access Node; -- use of incomplete declaration
type Node is record -- completion
Value : Integer;
Previous : Link;
Next : Link;
end record;
-- Here are some more variable declarations:
T : Time_Card;
D : Date;
Chart : Color_Chart;
S : Link;
P : Point;
begin
Count := Count + 1;
Put_Line ("Count = " & Count'Image & ", K = " & K'Image);
Put_Line ("See you every " & Tue'Image & " and " & Thu'Image);
-- Printing floats
Put (Tau, Exp => 0); New_Line;
Put (Tau, Fore => 7, Aft => 6, Exp => 5); New_Line;
-- Enumeration type attributes
Assert (Color'Last = White);
Assert (Color'Val (2) = Green);
Assert (Day'Pos (Sat) = 5 and then Day'Pos (Sun) = 6);
-- Array Aggregates
T := [2.6, 10.2, 9.3, 8.0, 0.0, 0.0, 1.0];
Assert (T (Wed) = 9.3);
Chart := [Black, Cyan, Red, Blue, others => Magenta];
Assert (Chart (3) = Blue);
-- Records
D := (1, Jul, 1991); Put_Line (D'Image);
D := (Month => Jul, Day => 1, Year => 1991); Put (D.Year);
D := (1, Year => 1991, Month => Jul);
Assert (D.Month = Jul);
P := (3.0, -234.22);
Put (P.X);
-- Strings
Put (Greeting);
New_Line;
Put_Line (C_Comment (68 .. 80)); -- this is called a slice
Assert (Ninety_Eight'Length = 6);
-- Pointers
S := new Node;
S.all.Value := 3;
S.Value := 3; -- ".all" is optional in middle
S.Next := new Node'(Value => 10, Next => null, Previous => S);
-- The Image attribute works even for arrays of arrays
Put_Line (Ident'Image);
end Simple_Types;
TODO - numeric literals
TODO - null
TODO - string literals
TODO - aggregates
TODO - names
TODO - allocators
TODO - parenthesized expressions
TODO - conditional expressions
TODO - quantified expressions
TODO - operators
For more, see the expressions lesson at learn.adacore.com.
We’ve seen a lot of the statements above, so for completeness, here’s a list of every kind of Ada statement:
Here’s another classic pedagogical exercise: creating an abstract data types for stacks, from first principles, using linked lists. The stack element type will be generic. First, the package specification:
generic
type Element is private;
package Unbounded_Stacks is
type Stack is limited private;
Overflow, Underflow, No_Top : exception;
function Is_Empty (S : Stack) return Boolean;
function Size (S : Stack) return Natural;
procedure Push (S : in out Stack; E : Element);
function Top (S : Stack) return Element;
procedure Pop (S : in out Stack);
procedure Pop (S : in out Stack; E : out Element);
private
type Node;
type Stack is access Node;
type Node is record
Data : Element;
Link : Stack;
end record;
end Unbounded_Stacks;
Next, the package body, in a separate file:
package body Unbounded_Stacks is
function Is_Empty (S : Stack) return Boolean is (S = null);
function Size (S : Stack) return Natural is
(if S = null then 0 else 1 + Size (S.Link));
procedure Push (S : in out Stack; E : Element) is
begin
S := new Node'(E, S);
exception
when Storage_Error => raise Overflow;
end Push;
function Top (S : Stack) return Element is
begin
if Is_Empty (S) then
raise No_Top;
end if;
return S.Data;
end Top;
procedure Pop (S : in out Stack) is
begin
if Is_Empty (S) then
raise Underflow;
end if;
S := S.Link;
end Pop;
procedure Pop (S : in out Stack; E : out Element) is
begin
if Is_Empty (S) then
raise Underflow;
end if;
E := S.Data;
S := S.Link;
end Pop;
end Unbounded_Stacks;
Now, if we want to add new functionality to our ADT, we can use a child package. This is a pretty decent illustration of when to use child packages, since it’s probably best not to pollute the basic stack package with I/O, which not everyone may want:
------------------------------------------------------------------------------
-- unbounded_stacks-io.ads
--
-- A generic child package which enables output for unbounded stacks.
--
-- Generic Parameters:
--
-- Put (F, E) procedure to display element E to file F.
--
-- Operations:
--
-- Put (F, S) write S to file F in the form <x1 x2 ... xn> from top
-- to bottom.
-- Put (S) Same as Put (Current_Output, S)
------------------------------------------------------------------------------
with Ada.Text_IO;
use Ada.Text_IO;
generic
with procedure Put (F : File_Type; E : Element);
package Unbounded_Stacks.IO is
procedure Put (F : File_Type; S : Stack);
procedure Put (S : Stack);
end Unbounded_Stacks.IO;
Here’s the body of the child package:
package body Unbounded_Stacks.IO is
procedure Put (F : File_Type; S : Stack) is
T : Stack := S;
begin
Put (F, "< ");
while T /= null loop
Put (F, T.Data);
Put (' ');
T := T.Link;
end loop;
Put (F, '>');
end Put;
procedure Put (S : Stack) is
begin
Put (Current_Output, S);
end Put;
end Unbounded_Stacks.IO;
To exercise the stack package, we write a little driver:
with Ada.Text_IO, Unbounded_Stacks, Unbounded_Stacks.IO;
use Ada.Text_IO;
procedure Test_Unbounded_Stacks is
package My_Stacks is new Unbounded_Stacks (Character);
use My_Stacks;
package My_Stacks_IO is new My_Stacks.IO (Put);
use My_Stacks_IO;
S : Stack;
procedure Test is
Option : Character;
Data : Character;
begin
loop
Put (S);
Put_Line (" Length =" & Size (S)'Image);
Put ("Empty pusH Pop Quit: ");
Get (Option);
New_Line;
case Option is
when 'h' =>
begin
Put ("push what? "); Get (Data); Push (S, Data);
exception
when Overflow => Put_Line ("Cannot push onto full stack");
end;
when 'p' =>
begin
Pop (S, Data); Put (Data); Put_Line (" popped");
exception
when Underflow => Put_Line ("Cannot pop from empty stack");
end;
when 'e' =>
Put (if Is_Empty (S) then "Empty" else "Not Empty");
when 'q' => exit;
when others => null;
end case;
New_Line;
end loop;
end Test;
begin
Test;
end Test_Unbounded_Stacks;
Ada’s sum type is the variant record. Here’s the classic shape type, where a shape can be a circle or a rectangle:
-- An approach to shapes using variant records
with Ada.Text_IO, Ada.Numerics;
use Ada.Text_IO, Ada.Numerics;
procedure Variant_Demo is
type Color is (Black, Blue, Green, Cyan, Red, Magenta, Yellow, White);
type Shape_Kind is (Circle, Rectangle);
type Shape (Kind : Shape_Kind := Circle) is record
Fill : Color;
case Kind is
when Circle => Radius : Float;
when Rectangle => Width, Height : Float;
end case;
end record;
function Area (S : Shape) return Float is
begin
case S.Kind is
when Circle => return Pi * S.Radius ** 2;
when Rectangle => return S.Width * S.Height;
end case;
end Area;
function Perimeter (S : Shape) return Float is
begin
case S.Kind is
when Circle => return 2.0 * Pi * S.Radius;
when Rectangle => return 2.0 * (S.Width + S.Height);
end case;
end Perimeter;
type Shape_Array is array (Positive range <>) of Shape;
Shapes : constant Shape_Array := [
Shape'(Kind => Circle, Fill => Red, Radius => 10.0),
Shape'(Kind => Rectangle, Fill => Blue, Width => 5.0, Height => 10.0)
];
begin
for Shape of Shapes loop
Put_Line (Shape.Kind'Image & ": Area = " & (Area (Shape)'Image) &
", Perimeter = " & (Perimeter (Shape)'Image));
end loop;
end Variant_Demo;
The term object-oriented programming has strayed from its original meaning to a world of in which objects have state and behavior, and in which the types (classes) are emphasized over the the functions. Ada does this with tagged records. (The term “tagged” makes sense here—the dynamic polymorphism of modern OOP happens because values are tagged with their “class”):
with Ada.Text_IO, Ada.Numerics, Ada.Tags;
use Ada.Text_IO, Ada.Numerics, Ada.Tags;
procedure Tagged_Demo is
type Color is (Black, Blue, Green, Cyan, Red, Magenta, Yellow, White);
package Shapes is
type Shape is abstract tagged record
Fill : Color;
end record;
function Area (S : Shape) return Float is abstract;
function Perimeter (S : Shape) return Float is abstract;
type Circle is new Shape with record
Radius : Float;
end record;
overriding function Area (C : Circle) return Float;
overriding function Perimeter (C : Circle) return Float;
type Rectangle is new Shape with record
Width, Height : Float;
end record;
overriding function Area (R : Rectangle) return Float;
overriding function Perimeter (R : Rectangle) return Float;
end Shapes;
package body Shapes is
overriding function Area (C : Circle) return Float is
(Pi * C.Radius ** 2);
overriding function Perimeter (C : Circle) return Float is
(2.0 * Pi * C.Radius);
overriding function Area (R : Rectangle) return Float is
(R.Width * R.Height);
overriding function Perimeter (R : Rectangle) return Float is
(2.0 * (R.Width + R.Height));
end Shapes;
use Shapes;
My_Shapes : constant array (Positive range <>) of access Shape'Class := [
new Circle'(Fill => Red, Radius => 10.0),
new Rectangle'(Fill => Blue, Width => 5.0, Height => 10.0)
];
begin
for Shape of My_Shapes loop
Put_Line (Expanded_Name (Shape'Tag)
& ": Area = " & Area (Shape.all)'Image
& ", Perimeter = " & Perimeter (Shape.all)'Image);
end loop;
end Tagged_Demo;
Variant Records vs. Tagged RecordsWith variant records (sum types), we see:
- A single type with multiple variants
- Logic in external functions that operate on the type
- Use of discriminated unions and case statements to branch on the variant
- A more procedural/functional programming style
With tagged records (OOP), we see:
- Multiple types in a class hierarchy
- Methods that belong to the types
- Use of inheritance and dynamic dispatching to select the appropriate method
- A more object-oriented programming style
TODO
TODO
Concurrency features have been built into Ada since its inception, and is one of the language's key strengths. The topic is so large that full-length books have been written about Ada concurrency.
My notes are on a separate page.
TODO
Ada features a really cool and comprehensive standard library. The full list of units is summarized at the beginning of Annex A of the Reference Manual. The library is organized into a hierarchy, which can be used to organize code and provide a namespace for identifiers.
There are three root library units: Ada, System, and Interfaces. Here’s a listing for scanning. See the Reference Manual index to dig deeper.
Ada — A.2
Assertions — 11.4.2
Asynchronous_Task_Control — D.11
Calendar — 9.6
Arithmetic — 9.6.1
Formatting — 9.6.1
Time_Zones — 9.6.1
Characters — A.3.1
Conversions — A.3.4
Handling — A.3.2
Latin_1 — A.3.3
Command_Line — A.15
Complex_Text_IO — G.1.3
Containers — A.18.1
Bounded_Doubly_Linked_Lists — A.18.20
Bounded_Hashed_Maps — A.18.21
Bounded_Hashed_Sets — A.18.23
Bounded_Indefinite_Holders — A.18.32
Bounded_Multiway_Trees — A.18.25
Bounded_Ordered_Maps — A.18.22
Bounded_Ordered_Sets — A.18.24
Bounded_Priority_Queues — A.18.31
Bounded_Synchronized_Queues — A.18.29
Bounded_Vectors — A.18.19
Doubly_Linked_Lists — A.18.3
Generic_Array_Sort — A.18.26
Generic_Constrained_Array_Sort — A.18.26
Generic_Sort — A.18.26
Hashed_Maps — A.18.5
Hashed_Sets — A.18.8
Indefinite_Doubly_Linked_Lists — A.18.12
Indefinite_Hashed_Maps — A.18.13
Indefinite_Hashed_Sets — A.18.15
Indefinite_Holders — A.18.18
Indefinite_Multiway_Trees — A.18.17
Indefinite_Ordered_Maps — A.18.14
Indefinite_Ordered_Sets — A.18.16
Indefinite_Vectors — A.18.11
Multiway_Trees — A.18.10
Ordered_Maps — A.18.6
Ordered_Sets — A.18.9
Synchronized_Queue_Interfaces — A.18.27
Unbounded_Priority_Queues — A.18.30
Unbounded_Synchronized_Queues — A.18.28
Vectors — A.18.2
Decimal — F.2
Direct_IO — A.8.4
Directories — A.16
Hierarchical_File_Names — A.16.1
Information — A.16
Dispatching — D.2.1
EDF — D.2.6
Non_Preemptive — D.2.4
Round_Robin — D.2.5
Dynamic_Priorities — D.5.1
Environment_Variables — A.17
Exceptions — 11.4.1
Execution_Time — D.14
Group_Budgets — D.14.2
Interrupts — D.14.3
Timers — D.14.1
Finalization — 7.6
Float_Text_IO — A.10.9
Float_Wide_Text_IO — A.11
Float_Wide_Wide_Text_IO — A.11
Integer_Text_IO — A.10.8
Integer_Wide_Text_IO — A.11
Integer_Wide_Wide_Text_IO — A.11
Interrupts — C.3.2
Names — C.3.2
IO_Exceptions — A.13
Iterator_Interfaces — 5.5.1
Locales — A.19
Numerics — A.5
Big_Numbers — A.5.5
Big_Integers — A.5.6
Big_Reals — A.5.7
Complex_Arrays — G.3.2
Complex_Elementary_Functions — G.1.2
Complex_Types — G.1.1
Discrete_Random — A.5.2
Elementary_Functions — A.5.1
Float_Random — A.5.2
Generic_Complex_Arrays — G.3.2
Generic_Complex_Elementary_Functions — G.1.2
Generic_Complex_Types — G.1.1
Generic_Elementary_Functions — A.5.1
Generic_Real_Arrays — G.3.1
Real_Arrays — G.3.1
Real_Time — D.8
Timing_Events — D.15
Sequential_IO — A.8.1
Storage_IO — A.9
Streams — 13.13.1
Storage_Streams — 13.13.1
Bounded_FIFO_Streams — 13.13.1
FIFO_Streams — 13.13.1
Stream_IO — A.12.1
Strings — A.4.1
Bounded — A.4.4
Equal_Case_Insensitive — A.4.10
Hash — A.4.9
Hash_Case_Insensitive — A.4.9
Less_Case_Insensitive — A.4.10
Equal_Case_Insensitive — A.4.10
Fixed — A.4.3
Equal_Case_Insensitive — A.4.10
Hash — A.4.9
Hash_Case_Insensitive — A.4.9
Less_Case_Insensitive — A.4.10
Hash — A.4.9
Hash_Case_Insensitive — A.4.9
Less_Case_Insensitive — A.4.10
Maps — A.4.2
Constants — A.4.6
Text_Buffers — A.4.12
Bounded — A.4.12
Unbounded — A.4.12
Unbounded — A.4.5
Equal_Case_Insensitive — A.4.10
Hash — A.4.9
Hash_Case_Insensitive — A.4.9
Less_Case_Insensitive — A.4.10
UTF_Encoding — A.4.11
Conversions — A.4.11
Strings — A.4.11
Wide_Strings — A.4.11
Wide_Wide_Strings — A.4.11
Wide_Bounded — A.4.7
Wide_Equal_Case_Insensitive — A.4.7
Wide_Hash — A.4.7
Wide_Hash_Case_Insensitive — A.4.7
Wide_Equal_Case_Insensitive — A.4.7
Wide_Fixed — A.4.7
Wide_Equal_Case_Insensitive — A.4.7
Wide_Hash — A.4.7
Wide_Hash_Case_Insensitive — A.4.7
Wide_Hash — A.4.7
Wide_Hash_Case_Insensitive — A.4.7
Wide_Maps — A.4.7
Wide_Constants — A.4.7
Wide_Unbounded — A.4.7
Wide_Equal_Case_Insensitive — A.4.7
Wide_Hash — A.4.7
Wide_Hash_Case_Insensitive — A.4.7
Wide_Wide_Bounded — A.4.8
Wide_Wide_Equal_Case_Insensitive — A.4.8
Wide_Wide_Hash — A.4.8
Wide_Wide_Hash_Case_Insensitive — A.4.8
Wide_Wide_Equal_Case_Insensitive — A.4.8
Wide_Wide_Fixed — A.4.8
Wide_Wide_Equal_Case_Insensitive — A.4.8
Wide_Wide_Hash — A.4.8
Wide_Wide_Hash_Case_Insensitive — A.4.8
Wide_Wide_Hash — A.4.8
Wide_Wide_Hash_Case_Insensitive — A.4.8
Wide_Wide_Maps — A.4.8
Wide_Wide_Constants — A.4.8
Wide_Wide_Unbounded — A.4.8
Wide_Wide_Equal_Case_Insensitive — A.4.8
Wide_Wide_Hash — A.4.8
Wide_Wide_Hash_Case_Insensitive — A.4.8
Synchronous_Barriers — D.10.1
Synchronous_Task_Control — D.10
EDF — D.10
Tags — 3.9
Generic_Dispatching_Constructor — 3.9
Task_Attributes — C.7.2
Task_Identification — C.7.1
Task_Termination — C.7.3
Text_IO — A.10.1
Bounded_IO — A.10.11
Complex_IO — G.1.3
Editing — F.3.3
Text_Streams — A.12.2
Unbounded_IO — A.10.12
Unchecked_Conversion — 13.9
Unchecked_Deallocate_Subpool — 13.11.5
Unchecked_Deallocation — 13.11.2
Wide_Characters — A.3.1
Handling — A.3.5
Wide_Command_Line — A.15.1
Wide_Directories — A.16.2
Wide_Environment_Variables — A.17.1
Wide_Text_IO — A.11
Complex_IO — G.1.4
Editing — F.3.4
Text_Streams — A.12.3
Wide_Bounded_IO — A.11
Wide_Unbounded_IO — A.11
Wide_Wide_Characters — A.3.1
Handling — A.3.6
Wide_Wide_Command_Line — A.15.1
Wide_Wide_Directories — A.16.2
Wide_Wide_Environment_Variables — A.17.1
Wide_Wide_Text_IO — A.11
Complex_IO — G.1.5
Editing — F.3.5
Text_Streams — A.12.4
Wide_Wide_Bounded_IO — A.11
Wide_Wide_Unbounded_IO — A.11
Interfaces — B.2
C — B.3
Pointers — B.3.2
Strings — B.3.1
COBOL — B.4
Fortran — B.5
System — 13.7
Address_To_Access_Conversions — 13.7.2
Atomic_Operations — C.6.1
Exchange — C.6.2
Integer_Arithmetic — C.6.4
Modular_Arithmetic — C.6.5
Test_And_Set — C.6.3
Machine_Code — 13.8
Multiprocessors — D.16
Dispatching_Domains — D.16.1
RPC — E.5
Storage_Elements — 13.7.1
Storage_Pools — 13.11
Subpools — 13.11.4
Please, read the Wikibook or go through the materials at learn.adacore.com. There wasn’t much covered here, though we hope you found it useful.