to test whether a value is in a given range, or

to test whether a value is in a given subtype.

Examples of these are

However, the restrictions
are annoying. If we want to test whether it is safe to eat an oyster
(there has to be an R in the month) then we would really like to write

Jan .. April | Sep .. Dec

whereas we are forced
to write something like

which means repeating
M and then perhaps worrying about whether
to use **or** or **or else**. Or in this case we could do the test
the other way

What we would really like to do is use the vertical
bar as in case statements and aggregates to select a combination of ranges,
subtypes, and values.

Ada 2012 is much more
flexible in this area. To see the differences it is probably easiest
to look at the old and new syntax. The relevant old syntax for Ada 2005
is

relation ::=

simple_expression [relational_operator simple_expression]

| simple_expression [**not**] **in** range

| simple_expression [**not**] **in** subtype_mark

simple_expression [relational_operator simple_expression]

| simple_expression [

| simple_expression [

where the last two
productions define membership tests. The syntax regarding choices in
aggregates and case statements in Ada 2005 is

discrete_choice_list ::= discrete_choice { | discrete_choice}

discrete_choice ::= expression | discrete_range | **others**

discrete_range ::= *discrete_*subtype_indication | range

The syntax in Ada 2012
is rather different and changes relation to
use new productions for membership_choice_list
and membership_choice (this enables the vertical
bar to be used in membership tests). And then a membership_choice
in turn uses choice_expression and choice_relation
as follows

relation ::=

simple_expression [relational_operator simple_expression]

| simple_expression [**not**] **in** membership_choice_list}

simple_expression [relational_operator simple_expression]

| simple_expression [

membership_choice_list ::=

membership_choice { | membership_choice}

membership_choice { | membership_choice}

membership_choice ::=

choice_expression | range | subtype_mark

choice_expression | range | subtype_mark

choice_expression ::=

choice_relation {**and** choice_relation}

| choice_relation {**or** choice_relation}

| choice_relation {**xor** choice_relation}

| choice_relation {**and then** choice_relation}

| choice_relation {**or else** choice_relation}

choice_relation {

| choice_relation {

| choice_relation {

| choice_relation {

| choice_relation {

choice_relation ::=

simple_expression [relational_operator simple_expression]}

simple_expression [relational_operator simple_expression]}

The difference between
a choice_relation and a relation
is that the choice_relation does not include
membership tests. Moreover, discrete_choice
is changed to

discrete_choice ::= choice_expression
| *discrete_*subtype_indication | range
| **others**

the difference being that a discrete_choice
now uses a choice_expression rather than an
expression as one of its possibilities.

The overall effect of the changes is to permit the
vertical bar in membership tests without getting too confused by nesting
membership tests.

Here are some examples
that are now permitted in Ada 2012 but were not permitted in Ada 2005

-- N

--

--

--

--

Membership tests are permitted for any type and values
do not have to be static. There is no change here but it should be remembered
that existing uses of the vertical bar in case statements and aggregates
do require the type to be discrete and the values to be static.

Another important point about membership tests is
that the membership choices are evaluated in order and as soon as one
is found to be true (or false if **not** is present) then the relation
as a whole is determined and the other membership choices are not evaluated.
This is therefore the same as using short circuit forms such as **or
else** and so gives another example of expressions which are statically
unevaluated.

There is one very minor
incompatibility. In Ada 2005 we can write

X: Boolean := ...

**case** X **is**

**when** Y **in** 1 .. 10 => F(10);

**when others** => F(5);

**end case**;

This is rather peculiar. The discrete choice Y
**in** 1 .. 10 must be static. Suppose
Y is 5, so that
Y **in** 1 .. 10
is True; then if X
is True, we call F(10)
whereas if X is False
we call F(5). And vice versa for values of
Y not in the range 1 to 10.

This is syntactically
illegal in Ada 2012 because a discrete choice can no longer be an expression
and so be a membership test. This was imposed because otherwise we might
have been tempted to write

X: Boolean := ...

**case** X **is**

**when** Y **in** 1 .. 10 | 20 => F(10);

**when others** => F(5);

**end case**;

and this is syntactically ambiguous because it might
be parsed as (Y **in** 1
.. 10) | 20 rather than as if we were allowed to write Y
**in** (1 .. 10) | 20. Although it would
be rejected anyway because of the type mismatch.

A nastier example might
make this clearer. Consider

Now suppose that Y itself
is of type Boolean. Is it (Y **in** False)
| True rather than Y **in** (False
| True)? If Y happens to have the value
True then the first interpretation gives False
| True so whatever the value of X we
always Do_This but in the second interpretation
we get just True so if X
happens to be False we Do_That.
So it really is seriously ambiguous without any type mismatch in sight
and has to be forbidden.

However, this is clearly very unlikely to be a problem.
Case statements over Boolean types are pretty rare anyway.

There is one other change to membership tests which
concerns access types and so will be considered again in Section 6.4
when we discussion access types and storage pools. The change is that
membership tests can be used to check accessibility.

It is often the case
that one uses a membership test before a conversion to ensure that the
conversion will succeed. This avoids raising an exception which then
has to be handled. Thus we might have

Total: Integer;

S: Score;

... --

S := Score(Total); --

... --

... --

If we are indexing some arrays whose range is Score
then it is an advantage to use S as an index
since we know it will work and no checks are needed.

However, in Ada 2005,
we cannot use a membership test to check accessibility. But Ada 2012
permits this and we can write

Local: Ptr;

Local := Ptr(X); --

... --

... --

We could also do the
check in a precondition thus

Here we have a precondition where the expression
is simply a membership test X **in** Ptr.
Of course this does not avoid the exception because it will raise Assertion_Error
if the accessibility is wrong.

Finally, note that two changes have been made in
the syntax for relation since ISO standardization. One concerns the addition
of a new form of expression, the raise expression; the other concerns
an ambiguity discovered in membership tests. These changes are described
in Section 9.5 of the Epilogue.

© 2011, 2012, 2013 John Barnes Informatics.

Sponsored in part by:

The Ada Resource Association: |
AdaCore: |
and Ada-Europe: |