Version 1.4 of ai05s/ai05-0188-1.txt

Unformatted version of ai05s/ai05-0188-1.txt version 1.4
Other versions for file ai05s/ai05-0188-1.txt

!standard 4.3.3(14)          10-02-03 AI05-0188-1/03
!standard 4.4(1)
!standard 4.5.8(0)
!standard 4.9(12)
!standard 4.9(33)
!standard 7.5(2.1/2)
!class amendment 09-11-03
!status work item 09-11-03
!status received 09-11-03
!priority Low
!difficulty Medium
!subject Case expressions
!summary
case expressions are added to Ada.
!problem
Conditional_expressions are added by AI05-0147-1. This proposal is to add an analogous contruct -- the case expression.
The full coverage rules for case statements and aggregates are a huge benefit to maintenance of Ada programs: If you add an enumeration literal, the compiler tells you about all the case statements and aggregates that need to be modified (assuming you don't defeat the full coverage rules by using "others").
In cases where conditional_expressions are useful, we don't want to lose the benefits of full coverage rules. A common example is in preconditions. Suppose we have:
procedure Add_To_Fruit_Salad( -- In a package spec. Fruit : in out Fruit_Type; Bowl : in out Bowl_Type);
procedure Add_To_Fruit_Salad( -- In the body. Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) is begin -- Check if ready to add to fruit salad case Fruit.Kind is when Apple => pragma Assert(Fruit.Is_Crisp); null; when Banana => pragma Assert(Fruit.Is_Peeled); null; when Pineapple => pragma Assert(Fruit.Is_Cored); null; end case; Cut_Up(Fruit); Add_To_Bowl(Fruit, Bowl); end Add_To_Fruit_Salad;
We would like to remove those Assert pragmas, and make them into preconditions:
procedure Add_To_Fruit_Salad( Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) with Pre => (if Fruit.Kind = Apple then Fruit.Is_Crisp elsif Fruit.Kind = Banana then Fruit.Is_Peeled elsif Fruit.Kind = Pineapple then Fruit.Is_Cored);
But then if we add Orange to the Fruit_Kind type, we might be missing a precondition. It would be better to write it like this:
procedure Add_To_Fruit_Salad( Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) with Pre => (case Fruit.Kind is when Apple => Fruit.Is_Crisp, when Banana => Fruit.Is_Peeled, when Pineapple => Fruit.Is_Cored);
Now if we add Orange, we will get an error, prompting us to add "when Orange => Fruit.Is_Juicy", or "when Orange => True", or whatever is appropriate.
Without case expressions, we are left with a nasty choice: put the assertions in the body, where they don't belong, or lose the full coverage rules.
!proposal
(See wording.)
!wording
Add a bullet following 4.3.3(14):
* For a conditional_expression or case_expression, the applicable index
constraint for each dependent_expression is that, if any, defined for the conditional or case_expression;
(Minor detail: AI05-0147-1 suggests this should go after (15), but it seems better after (14).)
Modify 4.4(1) to add conditional and case expressions, as follows:
In this International Standard, the term "expression" refers to a construct of the syntactic category expression or of any of the following categories: relation, simple_expression, term, factor, primary, conditional_expression, case_expression.
Add a new clause:
4.5.8 Case expressions
A case_expression selects for evaluation one of a number of alternative expressions; the chosen alternative is defined by the value of an expression.
Syntax
case_expression ::= (case expression is case_expression_alternative {, case_expression_alternative} )
case_expression_alternative ::= when discrete_choice_list => *dependent*_expression
Wherever the Syntax Rules allow an expression, a case_expression may be used in place of the expression, so long as it is immediately surrounded by parentheses.
[AARM: Note that the above is the same rule as for conditional_expressions; see 4.5.7 for further discussion.]
Name Resolution Rules
The expected type for the expression and the discrete_choices are as for case statements (see 5.4).
If a case_expression is expected to be of a type T, the expected type for each dependent_expression of the case_expression is T. If a case_expression shall resolve to a type T, each dependent_expression shall resolve to T.
Legality Rules
The expressions and discrete_ranges given as discrete_choices of a case_expression shall be static. Redundant[A discrete_choice others, if present, shall appear alone and in the last discrete_choice_list.]
The possible values of the expression shall be covered as for case statements (see 5.4).
As for case statements, two distinct discrete_choices of a case_expression shall not cover the same value.
If the expected type of a case_expression is any type in a class of types (instead of a particular type), all dependent_expressions of the case_expression shall have the same type.
If the expected type of a case_expression is a specific tagged type, all of the dependent_expressions of the case_expression shall be dynamically tagged, or none shall be dynamically tagged; the case_expression is dynamically tagged if all of the dependent_expressions are dynamically tagged, is tag-indeterminate if all of the dependent_expressions are tag-indeterminant, and is statically tagged otherwise.
AARM Note: There is nothing corresponding to the implicit "else False" for conditional_expressions.
Dynamic Semantics
For the execution of a case expression, the expression specified after case is first evaluated.
If the value of the expression is covered by the discrete_choice_list of some case_expression_alternative, then the dependent_expression of the _alternative is evaluated, and this is the value of the case_expression.
Otherwise (the value is not covered by any discrete_choice_list, perhaps due to being outside the base range), Constraint_Error is raised.
================
Add after 4.9(12):
* A case_expression all of whose expressions are static expressions;
AI05-0147-1 calls for the replacement of 4.9(33). Add one more bullet:
* a *dependent_*expression of a case_expression whose
expression is static and not covered by the corresponding discrete_choice_list.
Modify 7.5(2.1/2):
In the following contexts, an expression of a limited type is not permitted unless it is an aggregate, a function_call, [or ]a parenthesized expression or qualified_expression whose operand is permitted by this rule{, or a conditional_ or case_expression all of whose dependent_expressions are permitted by this rule}:
!discussion
!examples
!ACATS test
ACATS B and C tests are needed.
!appendix

From: Bob Duff
Sent: Tuesday, November 3, 2009  1:10 PM

Here's an AI on case expressions, to complement conditional expressions.

[This is version /01 of the AI - ED]

****************************************************************

From: Robert Dewar
Sent: Tuesday, November 3, 2009  1:19 PM

> Case expressions are added to Ada.

looks good to me, I am going to go ahead and implement this in GNAT

****************************************************************

From: Bob Duff
Sent: Wednesday, February 3, 2010  4:26 PM

Here's my homework on AI05-0188-1 Case expressions.

Minor changes to !problem, major changes to !wording.

[This is version /03 of the AI - Editor.]

****************************************************************

From: Tucker Taft
Sent: Wednesday, February 3, 2010  4:35 PM

Might you consider defining:

   conditional_expression ::= if_expression | case_expression

   if_expression ::= IF ...

   case_expression ::= CASE ...

This would seem more consistent, and would eliminate the awkward
"conditional_expression or case_expression" appearing all over the place.

****************************************************************

From: Bob Duff
Sent: Wednesday, February 3, 2010  5:19 PM

> Might you consider defining:
>
>    conditional_expression ::= if_expression | case_expression
>
>    if_expression ::= IF ...
>
>    case_expression ::= CASE ...

Yes, I might.

> This would seem more consistent, and would eliminate the awkward
> "conditional_expression or case_expression"
> appearing all over the place.

If we could get a concensus that both of these AIs are 'in', then we could
simplify them by combining them, and write the common wording stuff just once.

****************************************************************

From: Bob Duff
Sent: Wednesday, February 3, 2010  4:28 PM

The proposed syntax for case expressions separates alternatives with commas.
Robert thinks perhaps we should leave them out -- not sure. Comments?  I am
(mildly) in favor of keeping the commas.

****************************************************************

From: Robert Dewar
Sent: Wednesday, February 3, 2010  4:32 PM

I am not sure I like the commas, though that's what I have implemented for now,
consider

    A := (if    X = Red   then 3
          elsif X = Green then 4
          elsif X = Blue  then 5
          else                 6);

    A := (case X is
          when Red   => 3,
          when Green => 4,
          when Blue  => 5,
          others     => 6);

Or should we try to have a more parallel syntax

    A := (case X is
          when Red   => 3
          when Green => 4
          when Blue  => 5
          others     => 6);

****************************************************************

From: Tucker Taft
Sent: Wednesday, February 3, 2010  4:40 PM

I prefer the commas.  I don't think everyone is going to format their output the
way you have here, and it looks better to me to have commas when formatted as
follows:

    (case X is when A => 5, when B => 6, when others => 7)

If it were "else when" then I could see dropping the commas, but "when A => 5
when B => 6" is harder to read without the commas in my view.

****************************************************************

From: Robert Dewar
Sent: Wednesday, February 3, 2010  4:49 PM

OK, commas fine by me (and less implementation work for sure at this stage,
since already done).

****************************************************************

From: Gary Dismukes
Sent: Wednesday, February 3, 2010  5:40 PM

> Minor changes to !problem, major changes to !wording.

Looks good.

One spelling nit:

> Whereever the Syntax Rules allow an expression,

Whereever => Wherever

****************************************************************

From: Bob Duff
Sent: Wednesday, February 3, 2010  6:38 PM

Randy, do you take care of this sort of editorial thing?

Anyway, note that this typo was copied from AI-147, so should be fixed in both.
Unless we combine them!

****************************************************************

From: Randy Brukardt
Sent: Wednesday, February 3, 2010  7:56 PM

I'll take care of it.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, February 3, 2010  8:21 PM

...
> Add a bullet following 4.3.3(14):
>
> * For a conditional_expression or case_expression, the applicable index
>   constraint for each *dependent_*expression is that, if any, defined
>   for the conditional or case_expression;
>
> (Minor detail: AI05-0147-1 suggests this should go after (15), but it
> seems better after (14).)

I put it after (15) because these are semantically parenthesized expressions,
and as such, it seemed best to group them with it (and after it). I did that
consistently (for instance, in the wording for limited expressions). Not a big
deal either way.

...
> Modify 7.5(2.1/2):
>
> In the following contexts, an expression of a limited type is not
> permitted unless it is an aggregate, a function_call, [or ]a
> parenthesized expression or qualified_expression whose operand is
> permitted by this rule{, or a conditional_ or case_expression all of
> whose *dependent_*expressions are permitted by this rule}:
>
> AARM Note: There is nothing corresponding to the implicit "else False"
> for conditional_expressions.

This AARM note seems to be in the wrong place; I can't imagine what it has to do
with expressions of limited types. But it might make sense somewhere in 4.5.8.

Tucker later writes:

> Might you consider defining:
>
>   conditional_expression ::= if_expression | case_expression
>
>   if_expression ::= IF ...
>
>   case_expression ::= CASE ...
>
> This would seem more consistent, and would eliminate the awkward
"conditional_expression or
> case_expression" appearing all over the place.

That would be OK, but I would object to combining the sections for the kinds of
expressions. We keep the legality and semantic rules for case and if statements
separate, and I would think we want to do that here as well.

I also have to wonder if this solution works very well for
quantified_expressions, which also have to be referred to in many of these
places (although the semantics is somewhat different). Would it look weird to
combine two of the three new kinds of expressions, and not the third??

****************************************************************

From: Ed Schonberg
Sent: Wednesday, February 3, 2010  8:30 PM

> I also have to wonder if this solution works very well for 
> quantified_expressions, which also have to be referred to in many of 
> these places (although the semantics is somewhat different). Would it 
> look weird to combine two of the three new kinds of expressions, and 
> not the third??

The resolution rules for conditional expressions and case expressions are 
similar, but completely different from those of quantified expressions. The
latter are always boolean, and there is nothing in the context that impacts
the resolution of the container expression and the predicate, so they have
to have their own section.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, February 3, 2010  8:43 PM

One more thing... (unless Steve Jobs has that trademarked :-)

> Bob Duff writes:

>Add after 4.9(12):
>
>* A case_expression all of whose conditions and dependent_expressions are
>  static expressions;

There aren't any conditions in a case_expression, so this is clearly wrong.
There isn't a name for the expression that controls the choice, so we can't
say that here. But I suppose that we don't need to differentiate here:

* A case_expression all of whose expressions are static expressions;

seems to be good enough.

****************************************************************

Questions? Ask the ACAA Technical Agent