Ada: Expressions

An expression combines literals, names, function calls, and operators to compute one value of one type. Ada's twist is that the type system follows the expression everywhere: arithmetic operators never silently convert, mixing types in one expression is a compile error, and every operator is overloaded per type.

Operators and Precedence

Ada has six precedence classes, from highest to lowest:

ClassOperatorsExample
Highest (unary)abs, not, **-X, abs X, 2 ** 10
Multiplying*, /, mod, remA * B, X mod 10
Binary adding+, -, &A + B, First & Last
Relational=, /=, <, <=, >, >=A /= B (note: /= means "not equal")
Logical (short circuit)and then, or elsePresent and then Valid
Logicaland, or, xor, in, not inN in 1 .. 10

Two operators deserve immediate attention because they encode decisions most languages leave unsafe:

  • and then short-circuits: if the left side is False the right side is never evaluated. Use it whenever the right side would crash on invalid input.
  • in tests membership in a range or subtype — the natural spelling of validation.

Integer Division and Remainders

Ada distinguishes mod (result has the sign of the divisor) from rem (sign of the dividend). Both are total: division by zero raises Constraint_Error, there is no undefined behavior:

with Ada.Text_IO; use Ada.Text_IO;

procedure Remainder_Demo is
begin
   Put_Line (Integer'Image (7 mod 3));    --  1   sign of divisor (+)
   Put_Line (Integer'Image ((-7) mod 3)); --  2   wraps up to the divisor's sign
   Put_Line (Integer'Image (7 rem 3));    --  1   sign of dividend (+)
   Put_Line (Integer'Image ((-7) rem 3)); -- -1   truncates toward zero
   --  Put_Line (Integer'Image (7 / 0));  -- Constraint_Error, never UB
end Remainder_Demo;

If you need the mathematical "wrap around a circle" behavior, mod is always right; rem matches what C's % does.

Type Safety in Expressions

Operators belong to types. + for Integer and + for Float are different overloads, and operands must agree:

procedure Mixing_Demo is
   Count : Integer := 3;
   Rate  : Float   := 2.5;
begin
   --  declare
   --     Bad : Float := Count * Rate;   -- compile error: Integer * Float
   --  is rejected: the compiler will not guess. State the conversion:
   declare
      Good : constant Float := Float (Count) * Rate;   -- 7.5
   begin
      null;
   end;
end Mixing_Demo;

Universal expressions are the one exception: numeric literals and named numbers can appear on either side because they belong to no type until the context fixes one.

Membership and Ranges

in and not in read like the validation they perform:

with Ada.Text_IO; use Ada.Text_IO;

procedure Membership_Demo is
   Reading : constant Integer := 47;
begin
   if Reading in 40 .. 80 then          -- inclusive range test
      Put_Line ("within operating band");
   end if;

   if Reading not in 1 .. 100 then      -- the readable negation
      Put_Line ("out of range");
   end if;
end Membership_Demo;

Next: Conditionals — turning these expressions into decisions.