!standard 4.5.7(0) 10-06-03 AI05-0188-1/05 !standard 4.5.8(0) !standard 4.9(33) !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. We also change the name conditional_expression to if_expression, and use conditional_expression to refer to both if_expression and 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 if_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 Rename 4.5.7 as added by AI05-0147-1 to If Expressions Modify the syntax rules in 4.5.7 added by AI05-0147-1: Change conditional_expression to if_expression. Add: conditional_expression ::= if_expression | case_expression In the Legality Rules, Static Semantics, and Dynamic Semantics rules of 4.5.7 change conditional_expression to if_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 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. AARM To Be Honest: T in this rule could be any type in a class of types (including the class of all types), or (for the second rule) an anonymous access type (for renames) or a universal type covering some type (for qualified expressions). 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 if_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. ================ 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. The existing bullet is changed to discuss if_expressions rather than conditional_expression. !discussion (See !problem.) !examples (See !problem.) !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. **************************************************************** From: Bob Duff Sent: Wednesday, February 24, 2010 2:41 PM New version of AI05-0188-1, "Case expressions". I'm not sure this is entirely correct, since it's supposed to depend on AI05-0147-1, which Tucker has not yet updated. I am assuming that AI05-0147-1 covers these issues, with wording that still works for this AI (AI05-0188-1). - applicable index constraint - Wording similar to this: Modify 4.4(1) to add conditional_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. - 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.] - Static expressions (4.9(12, 33)): - 7.5(2.1/2) -- "In the following contexts, an expression of a limited type is not permitted unless...." [Following was version /04 of this AI - Editor.] **************************************************************** From: Steve Baird Sent: Wednesday, February 24, 2010 5:00 PM > 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. > The corresponding wording for if_expressions (formerly conditional_expressions) has a TBH note because it was decided that saying what we really mean here is too much trouble. (Just to recap, I'm referring to the interaction between this wording and, e.g., the rule for qualified expressions (4.7(3)) or for conditions (5.3(4))). Don't we need to do that again here? **************************************************************** From: Bob Duff Sent: Wednesday, February 24, 2010 5:50 PM Yeah, or better yet, make sure the if_expr AI is worded in a way that obviates the need for duplicating the wording for case_exprs. That's Tuck's job. I was told to make the case_expr AI depend on the if_expr AI, but not the other way 'round. But the if_expr AI needs to "secretly" depend on the case_expr AI, in the sense that the wording has to be carefully crafted to cover case_exprs. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 24, 2010 6:02 PM ... >Yeah, or better yet, make sure the if_expr AI is worded in a way that >obviates the need for duplicating the wording for case_exprs. That's Tuck's job. Bob, there is no open homework on AI05-0147-1. It's (supposedly) finished as I posted it (sometime after Feb 4). I believe I extracted the needed wording from Tuck right after the last meeting. So if there is something that needs to be changed about it, please tell us. **************************************************************** From: Randy Brukardt Sent: Wednesday, February 24, 2010 9:27 PM > - Static expressions (4.9(12, 33)): 4.9(33) is about unselected branches in if_expressions. That depends on the definition of "condition" and the execution of if statements. That wording cannot be made to work for case expressions, because the terminology for the two kinds of statements is completely different (you have to use something about "covers"). I left that wording in the draft! We need some wording in here to say that 4.5.7 as added by AI-147-1 is renamed to If expressions, and all uses of conditional_expression in Legality Rules, Static Semantics, and Dynamic Semantics are changed to if_expression. I added that. ****************************************************************