Version 1.5 of 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( --
Fruit : in out Fruit_Type; Bowl : in out Bowl_Type);
procedure Add_To_Fruit_Salad( --
Fruit : in out Fruit_Type; Bowl : in out Bowl_Type) is
begin
--
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: Pascal Leroy
Sent: Monday, November 16, 2009 3:53 PM
> 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);
At first I liked the if-expressions: I have often wanted them, and had to
simulate them with ugly work-arounds involving Boolean'Pos and the like. Then
of course I see the point about having a case-expression where you have the
benefits of the coverage rules, and you preserve the symmetry among the choices.
I am starting to feel uncomfortable, though, because there is no telling where
this will stop. Why not loop-expressions, for instance?
procedure P (S : String)
with Pre => (for I in S'Range loop if S(I) > 'a' then false);
Also, if you have a bunch of subprograms that all have the same pre- or
post-conditions, are you going to repeat the same expression over and over
again? It seems to me that it's virtually impossible to reuse pre- and
post-conditions. That's not good. As Bob likes to point out, the subprogram is
the fundamental unit of code reuse, and it's unfortunate to have to give up on
it. What I would really like to write is something like:
function Fruit_Is_Ready (Fruit : Fruit_Type) return Boolean renames -- Or whatever syntax.
(case Fruit.Kind is
when Apple => Fruit.Is_Crisp,
when Banana => Fruit.Is_Peeled,
when Pineapple => Fruit.Is_Cored);
procedure Add_To_Fruit_Salad(
Fruit : in out Fruit_Type; Bowl : in out Bowl_Type)
with
Pre => Fruit_Is_Ready(Fruit);
Of course, this would probably be more complicated to describe and to
implement...
****************************************************************
From: Randy Brukardt
Sent: Monday, November 16, 2009 4:06 PM
...
> I am starting to feel uncomfortable, though, because there is no
>telling where this will stop. Why not loop-expressions, for instance?
That's AI05-0176-1 (Quantified expressions). Thank Ed for that one.
> Also, if you have a bunch of subprograms that all have the same
> pre- or post-conditions, are you going to repeat the same expression
>over and over again? It seems to me that it's virtually impossible to
>reuse pre- and post-conditions. That's not good. As Bob likes to
>point out, the subprogram is the fundamental unit of code reuse, and
>it's unfortunate to have to give up on it. What I would really like
>to write> is something like:
>
> function Fruit_Is_Ready (Fruit : Fruit_Type) return Boolean renames
-- Or whatever syntax.
> (case Fruit.Kind is
> when Apple => Fruit.Is_Crisp,
> when Banana => Fruit.Is_Peeled,
> when Pineapple => Fruit.Is_Cored);
That's AI05-0177-1, renaming of expressions as functions. Thank me for pushing
this idea (although I think it might have been yours originally, many years
ago).
> Of course, this would probably be more complicated to describe and
> to implement...
Not really; it's the same as default expressions; the resolution is the same as
preconditions. Both of those are already done.
Anyway, is there any concern here that is not already on the agenda?? :-)
It certainly is true that this creeping featurism is what is worrying me, and
Bob, and several others. We'll probably have to do some work to reign that in.
But at this stage, it is more important to have all of the ideas on table so we
can weight them properly.
****************************************************************
From: Robert Dewar
Sent: Monday, November 16, 2009 4:09 PM
> I am starting to feel uncomfortable, though, because there is no
> telling where this will stop. Why not loop-expressions, for instance?
>
> procedure P (S : String)
> with Pre => (for I in S'Range loop if S(I) > 'a' then false);
Well indeed, this is the case of quantifiers, which are being actively
considered, and indeed go along with the other forms.
> function Fruit_Is_Ready (Fruit : Fruit_Type) return Boolean renames --
> Or whatever syntax.
> (case Fruit.Kind is
> when Apple => Fruit.Is_Crisp,
> when Banana => Fruit.Is_Peeled,
> when Pineapple => Fruit.Is_Cored);
>
> procedure Add_To_Fruit_Salad(
> Fruit : in out Fruit_Type; Bowl : in out Bowl_Type)
> with
> Pre => Fruit_Is_Ready(Fruit);
>
> Of course, this would probably be more complicated to describe and to
> implement...
I'm a bit confused, I don't quite understand your suggested syntax here, but
most certainly functional abstraction is possible in pre and post conditions.
****************************************************************
From: Jean-Pierre Rosen
Sent: Monday, November 16, 2009 4:51 PM
> It certainly is true that this creeping featurism is what is worrying
> me, and Bob, and several others.
Count me in
> We'll probably have to do some work to reign that in. But at this
> stage, it is more important to have all of the ideas on table so we
> can weight them properly.
Hmmm... Time for a "zero based budget" ?
****************************************************************
From: Bob Duff
Sent: Monday, November 16, 2009 4:57 PM
> It certainly is true that this creeping featurism is what is worrying
> me, and Bob, and several others.
Yeah, but I notice that we don't all agree on which of those creeps should be
squelched. E.g. I find case expressions significantly more useful than the +:=
thing, and not that much more difficult to implement.
I don't even agree with myself from week to week. ;-)
I think there's a lot on the table, though not as much as for Ada 2005.
****************************************************************
From: Robert Dewar
Sent: Monday, November 16, 2009 5:09 PM
>> It certainly is true that this creeping featurism is what is worrying
>> me, and Bob, and several others.
>
> Yeah, but I notice that we don't all agree on which of those creeps
> should be squelched. E.g. I find case expressions significantly more
> useful than the +:= thing, and not that much more difficult to implement.
Well not from where I sit. Case expressions is more like a days work, the +:= is
more like an hour or two, but still in the very easy category.
> I don't even agree with myself from week to week. ;-)
>
> I think there's a lot on the table, though not as much as for Ada 2005.
Well I am the first to get worried about an excessive implementation burden, or
gratuitous upward incompatibilities (we still have customers staying away from
Ada 2005, perhaps for ever, because of the limited return imcompatibility). But
so far the 2012 changes proposed seem very much in reasonable range (I have not
seen the real time stuff, which is effectively optional anyway).
****************************************************************
From: Pascal Leroy
Sent: Tuesday, November 17, 2009 12:56 AM
> Anyway, is there any concern here that is not already on the agenda?? :-)
Sorry for missing that this stuff was already being worked on. I have a hard
time keeping track of all the ideas that are thrown around. And I certainly
don't have a "big picture" of where this Amendment is going.
****************************************************************
From: Edmond Schonberg
Sent: Tuesday, November 17, 2009 12:05 PM
> Anyway, is there any concern here that is not already on the agenda??
> :-)
>
> Sorry for missing that this stuff was already being worked on. I have
> a hard time keeping track of all the ideas that are thrown around.
> And I certainly don't have a "big picture" of where this Amendment is
> going.
It is going in the direction of safety, of course! That means that
pre/postconditions and type invariants are the most significant additions. To
support these there is a new syntax for aspects of entities, and it follows that
there is also improved syntax for predicates of various sorts: if-expressions,
case expressions, quantified expressions. Then (perhaps) there are new forms of
constraints and non-contiguous discrete subtypes. More routine additions include
new container types.
New syntax for assignment operations is way down the list compared with the
above, in my opinion.
****************************************************************
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