[Ada Information Clearinghouse]
Ada '83 Rationale, Sec 3.11: Case Statements

"Rationale for the Design of the
Ada® Programming Language"

[Ada '83 Rationale, HTML Version]

Copyright ©1986 owned by the United States Government. All rights reserved.
Direct inquiries to the Ada Information Clearinghouse at adainfo@sw-eng.falls-church.va.us.

CHAPTER 3: Classical Programming

3.11 Case Statements

A case statement selects for execution one of a number of alternative sequences of statements: the alternative selected is defined by the value of an expression. Each sequence of statements is preceded by a list of choices defining the values for which that alternative must be selected. The main criteria in the design of the case statement are reliability and generality.

For reliability, the compiler must be given the opportunity to check for the accidental omission of some alternatives. For that reason, Ada requires that all possible values of the type of the discriminating expression be provided for in the choices. This rule is weakened if the discriminating expression is a name whose subtype is static: the choices that must be provided are then all the values of this subtype. Finally, a qualified expression can be used to restrict the possible choices, and a final choice others may be used to represent all values not previously specified.

As an example consider the declarations

type DAY is (MON, TUE, WED, THU, FRI, SAT, SUN);
subtype WORKDAY  is DAY range MON .. FRI;
subtype RESTDAY  is DAY range SAT  .. SUN;


With the above declarations all values of the type DAY (the type of TODAY) must appear in one selection, as in

case TODAY is
  when MON | TUE | WED | THU | FRI =>  WORK;
  when SAT | SUN =>  REST;
end case;

This could have been written in the equivalent form

case TODAY is
  when MON | TUE | WED | THU | FRI => WORK;
  when others => REST;
end case;

If in a given context it is known that the case discriminant belongs to a given subtype, a case statement with a qualified expression may be used. Only the values of the corresponding subtype can appear in the selections.

  when MON | WED | FRI =>  LATE;
  when TUE  | THU =>  EARLY;
end case;

Should the value of TODAY not belong to the subtype WORKDAY (for example if TODAY = SAT), then the exception CONSTRAINT_ERROR would be raised by the evaluation of the qualified expression. This cannot arise in the following example, which uses the fact that the subtype of START is static:

case START is
  when MON  | WED | FRI =>  LATE;
  when TUE   | THU =>  EARLY;
end case;

The other main criterion in the design of case statements is generality: the syntax of selections should accommodate all situations that are likely to arise, given that the case discriminant has a discrete type. Hence it should permit ranges as well as lists of values.

Thus the first example above is more likely to be written using ranges:

case TODAY is
  when MON .. FRI     =>  WORK;
  when SAT  .. SUN    =>  REST;
end case;

or (better) using the subtype names:

case TODAY is
  when WORKDAY  =>  WORK;
  when RESTDAY  =>  REST;
end case;

Such ranges and subtype names are very useful for case choices. They avoid long lists that can be tedious to read and therefore error- prone.

In many ways a case statement is similar to an array of statements and this is somewhat reflected in the syntax. For example we may compute the opposite of a given direction by means of a case statement:

  ...          -- a value is given to COURSE
case COURSE is
  when NORTH =>  BACK :=  SOUTH;
  when WEST  =>  BACK :=  EAST;
  when SOUTH =>  BACK :=  NORTH;
  when EAST  =>  BACK :=  WEST;
end case;
  -- now BACK is the direction opposite to COURSE

Another formulation of this computation uses an array of directions declared as

INVERSE : constant array(DIRECTION) of DIRECTION :=
                   (NORTH =>  SOUTH,
                    WEST  =>  EAST,
                    SOUTH =>  NORTH,
                    EAST  =>  WEST);

and the assignment statement


As can be seen from the above example, the conceptual similarity is actually reflected in the similarity of the syntaxes for case statements and for array aggregates.

A very important diagnostic facility that the compiler should provide is the listing of all values of the discriminating type that do not appear in the listed choices. For an incomplete (and therefore incorrect) case statement, the compiler has the information and should provide it to the programmer. In the absence of this kind of diagnostic, it might be quite difficult for the programmer to discover missing values for an enumeration type with a large number of values.

Case statements are conventionally implemented with an implicit transfer table. This table will generally contain one place for each possible value of the discriminating type. Quite often however, if some of the alternatives include null statements, the compiler may optimize the code generated, by using a shorter table and an explicit range check. As an example

case TODAY is
  when SAT  =>  SHOP;
  when SUN  =>  SLEEP;
  when others    =>  null;
end case;

may be compiled to produce code equivalent to

if TODAY in RESTDAY then
  case RESTDAY'(TODAY) is            -- no check needed
    when SAT  =>  SHOP;
    when SUN  =>  SLEEP;
  end case;
end if;

thus leading to a two-place transfer table. Finally, case statements with very sparse selections or with ranges as selections can be compiled as equivalent if statements. Thus for our first example we may have:

if TODAY in WORKDAY then
end if;

Address any questions or comments to adainfo@sw-eng.falls-church.va.us.