!standard 4.5.7(5/3) 19-06-15 AI12-0214-2/02 !standard 5.4(2/3) !class Amendment 18-05-16 !status work item 18-05-16 !status received 18-04-12 !priority Low !difficulty Easy !subject Boolean conditional case expressions and statements !summary A new form of case expression/statement, called the Boolean conditional case, is introduced. !problem It is common, especially in postconditions, to write a series of mutually exclusive conditions. If those conditions can't be mapped to a case expression, then an if_expression has to be used, losing the mutually exclusive property of the conditions. For example, we have to write: (if X > 5 then ... elsif X < 5 then ... elsif X = 6 then ...) But the property that the choices are supposed to exclusive is lost, so the error above (6 rather than 5 in the last expression) is lost. !proposal A Boolean conditional case expression consists of a number of alternatives guarded by Boolean conditions. The conditions are such that only one is true for any execution of a Boolean conditional case expression. If one is true, the associated dependent_expression is executed. If none are true or more than one are true, then Program_Error is raised. The same basic rules applies to Boolean conditional case statements with dependent_expressions replaced by statements. !wording Replace 4.5.7(5/3): case_expression ::= case selecting_expression is case_expression_alternative {, case_expression_alternative} | case is conditional_case_expression_alternative {, conditional_case_expression_alternative} Add after 4.5.7(6/3): conditional_case_expression_alternative ::= when condition_list => dependent_expression choice_condition ::= choice_expression condition_choice_list ::= choice_condition {| choice_condition} [Editor's note: We need "choice_condition" to avoid syntax ambiguity with memebrships] Modify 4.5.7(14/3): A condition is expected to be of any boolean type. {A choice_condition is expected to be of type Boolean.} AARM Reason: We want all of the choices of a case expression to be of the same type. Modify 4.5.7(21/3): For the evaluation of a case_expression {with a selecting_expression}, the ... Add after 4.5.7(21/3): For the evaluation of a case_expression without a selecting_expression, all of the choice_expressions are evaluated. If exactly one choice_expression is true, the dependent_expression of the conditional_case_expression_alternative containing this choice_condition is evaluated, converted to the type of the case_expression, and the resulting value is the value of the case_expression. Otherwise (no choice_expression is True, or multiple choice_expressions are True), Program_Error is raised. AARM Ramification: This is not a check! It can't be suppressed as we would not know what value to return in such a case. This is consistent with other case exceptions. [I suppose we could declare this a check and let it be suppressed -- a new check name would be needed -- execution would be erroneous and implementers could do whatever. That would be different than other case errors but it might make sense in this case [groan - pun] given that the disjointness check could cause issues in fielded systems; and one might have used an external tool to prove that the check can't fail.] Modify 4.9(12.1/3): a conditional_expression all of whose conditions, selecting_expressions, choice_expressions, and dependent_expressions are static expressions; Modify 4.9(32.5/3): a dependent_expression of a case_expression whose associated choice_condition is static and whose value evaluating to False; or ----- Replace 5.4(2/3) with: case_statement ::= case selecting_expression is case_statement_alternative {case_statement_alternative} end case; | case is conditional_case_statement_alternative {conditional_case_statement_alternative} end case; Add after 5.4(3) with: conditional_case_statement_alternative ::= when condition_list => sequence_of_statements Modify 5.4(11/3): For the execution of a case_statement {with a selecting_expression,} the selecting_expression is first evaluated. Add after 5.4(13/3): For the evaluation of a case_statement without a selecting_expression, all of the choice_expressions are evaluated. If exactly one choice_expression is true, the sequence_of_statements of the conditional_case_statement_alternative containing this choice_condition is executed. Otherwise (no choice_expression is True, or multiple choice_expressions are True), Program_Error is raised. AARM Ramification: This is not a check! It can't be suppressed as we would not know what value to return in such a case. This is consistent with other case exceptions. !discussion The disjointness exception could be a hazard in some cases. We could avoid that by making the rule be that the first choice_condition that is True is the one selected. But then there would be no advantage over a regular if expression. Part of the point of this feature is to tell external static analysis tools that these alternatives are disjoint -- if we didn't enforce that it really would not be true. !example The example in the Problem statement could be written: (case is when X > 5 => ... when X < 5 => ... when X = 6 => ...) and Program_Error would be raised if X = 6 (two alternatives being selected), exposing the mistake. ----- Along with AI12-0280-2, this new case expression greatly reduces the need for something like the Contract_Cases aspect as discussed in AI12-0280-1. For instance, a Contract_Cases example of: function T_Increment (X : T) return T with Global => null, Pre => X /= Max, Contract_Cases => (X.Seconds < Seconds_T'Last => T_Increment'Result.Seconds = X.Seconds + 1 and then T_Increment'Result.Minutes = X.Minutes and then T_Increment'Result.Hours = X.Hours, X.Seconds = Seconds_T'Last and then X.Minutes < Minutes_T'Last => T_Increment'Result.Seconds = 0 and then T_Increment'Result.Minutes = X.Minutes + 1 and then T_Increment'Result.Hours = X.Hours, X.Seconds = Seconds_T'Last and then X.Minutes = Minutes_T'Last => T_Increment'Result.Seconds = 0 and then T_Increment'Result.Minutes = 0 and then T_Increment'Result.Hours = X.Hours + 1); Could be written as the following given the new facilities of this AI (we don't need AI12-0280-2 facilities in this example): function T_Increment (X : T) return T with Global => null, Pre => X /= Max, Post => (case is when X.Seconds'Old < Seconds_T'Last => T_Increment'Result.Seconds = X.Seconds + 1 and then T_Increment'Result.Minutes = X.Minutes and then T_Increment'Result.Hours = X.Hours, when X.Seconds'Old = Seconds_T'Last and then X.Minutes'Old < Minutes_T'Last => T_Increment'Result.Seconds = 0 and then T_Increment'Result.Minutes = X.Minutes + 1 and then T_Increment'Result.Hours = X.Hours, when X.Seconds'Old = Seconds_T'Last and then X.Minutes'Old = Minutes_T'Last => T_Increment'Result.Seconds = 0 and then T_Increment'Result.Minutes = 0 and then T_Increment'Result.Hours = X.Hours + 1); !ASIS ** New ASIS functions needed ** TBD. !ACATS test ACATS B- and C-Tests are needed to check that the new capabilities are supported. !appendix From: Randy Brukardt Sent: Saturday, April 14, 2018 7:22 PM ... > Alternative proposals are always of interest, but (as I'm sure you'd > agree) ya gotta give me something more concrete than that. I had an idea when I woke up this morning. So here's a quick outline of the ideas (two). ... [Skipped the other idea, see AI12-0280-2 for that - Editor.] For checking disjoint cases in a postcondition, I suggest the "alternative" expression. (I thought about calling it "select", but then a statement version is impossible, so I think it needs a new keyword.) The example in the AI would look like using this (and the 'Old suggestion above): procedure Incr_Threshold_1 (X : in out Integer; Threshold : in Integer) with Pre => (X <= Threshold), Post => (alternative when X'Old < Threshold => X = X'Old + 1, when X'Old = Threshold => X = Threshold); I don't think we could reasonably overload "case" this way, thus a new keyword is needed. The semantics would include a disjointness check (only one of the choices could be true); otherwise it would essentially be an if statement and the extra construct wouldn't help). One would expect static analysis tools to do that check statically (and even a compiler could do that and warn appropriately). The full syntax would be something like: (alternative when {, } => {, when {, } => ) It would be a conditional expression, so the same rules on resolution and parens, and especially the 'Old rules proposed above, would apply. This is better than the previous proposal because: (1) More generally useful, anyone can use it anywhere (for instance, for composite types); (2) Doesn't mess with the model of Pre/Post; (3) Doesn't harm the readability of the precondition for the caller (the more important usage). This last point is rather important. The caller needs to know what restrictions there are on a call. A bunch of separate expressions is just confusing for that purpose, especially when the set is actually complete and represent no restriction. For instance, if you have something like: procedure Oper (X : in out Integer; Threshold : in Integer) with Pre => True, Post => (alternative when X'Old < Threshold => ..., when X'Old = Threshold => ..., when X'Old > Threshold => ...); The alternatives represent no restriction on X at all. Having to look through a mass of Contract_Cases to verify that is nasty. Consider the time example Tucker sent earlier: function T_Increment (X : T) return T with Global => null, Pre => X /= Max, Contract_Cases => (X.Seconds < Seconds_T'Last => T_Increment'Result.Seconds = X.Seconds + 1 and then T_Increment'Result.Minutes = X.Minutes and then T_Increment'Result.Hours = X.Hours, X.Seconds = Seconds_T'Last and then X.Minutes < Minutes_T'Last => T_Increment'Result.Seconds = 0 and then T_Increment'Result.Minutes = X.Minutes + 1 and then T_Increment'Result.Hours = X.Hours, X.Seconds = Seconds_T'Last and then X.Minutes = Minutes_T'Last => T_Increment'Result.Seconds = 0 and then T_Increment'Result.Minutes = 0 and then T_Increment'Result.Hours = X.Hours + 1); -- Provides part of S.count Here the "cases" don't represent any restriction on X, but verifying that (especially without any comments to that effect!!!) is difficult. (And the whole point of using contracts is to get away from comments; if you need a comment to describe the effective precondition, something is wrong!) The suggestions I have allow writing the same thing without messing with the precondition. Anyway, my $50 worth. (I wrote this note on-the-clock :-). **************************************************************** From: Tucker Taft Sent: Sunday, April 15, 2018 9:24 PM These are both interesting ideas. The 'Old idea seems like a good improvement, and probably not very controversial. Some kind of uniqueness-requiring conditional expression/statement does seem an interesting way to generalize Contract_Cases. I am worried about "alternative" as a new reserved word. It feels like one of those words that might be used heavily in some programs, probably as a type name. An alternative to "alternative" might be "case is" -- that is, omit the expression of a case statement. Hence: case is when X > 5 => ... when X = 5 => ... when X < 5 => ... end case; **************************************************************** From: Tucker Taft Sent: Tuesday, May 15, 2018 9:38 PM Given there is also some interest in having a case statement over composite objects, I wonder if we could kill two birds with one stone. In particular, we could relax the requirement that all choices in a case statement need to be static, and instead require that, if the choices are *not* all static, rather than requiring that they be non-overlapping, we require that exactly one case alternative matches at run-time, raising Program_Error (or Constraint_Error?) if not. Then for this "contract-case"-inspired case statement, we could simply write: case True is when X > 5 => ... when X = 5 => ... when X < 5 => ... end case; This would eliminate the need for any syntax change, and the semantic change would be to allow non-static expressions, but in that case require that exactly one of them is equal to the case expression, or in the presence of a "when others", at most one is equal to the case expression. In all cases equality would be determined using the primitive "=" of the type with signature T = T -> Boolean. .If there is no such primitive, the case statement is illegal. Ranges would be permitted for scalar types, and then the normal "membership" operation would be used This generalization would also allow simple composite matching: case X is when A => when B => when others => end case; where the type of X, and hence also of A and B, could be any type with a primitive "=" with the appropriate signature. To avoid surprises when the case choices suddenly end up all static (e.g. in a generic instantiation), we could allow overlap of such choices so long as the case expression is similarly static (e.g. "case True is"), and only complain if two overlapping static choices match the static case expression. The key requirement is that exactly one alternative can be chosen at run-time, with no dependence on the order of matching (except that "when others" is always last). Comments? I am happy to write up this refinement of this AI, if there is some interest. **************************************************************** From: Jean-Pierre Rosen Sent: Tuesday, May 15, 2018 11:55 PM > The key requirement is that exactly one alternative can be chosen at > run-time, with no dependence on the order of matching (except that > "when others" is always last). Interesting idea, but what do you mean by "no dependence on the order of matching" ? If function calls are allowed, you certainly depend on the order! **************************************************************** From: Tucker Taft Sent: Wednesday, May 16, 2018 8:25 AM What I meant was that you don't "stop" when you get to a match. Instead, you keep going, and complain if there are multiple matches. So the order doesn't affect which one of the matching alternatives you choose, since we limit it to one match. But you are right, if there are function calls (including the call on the primitive "=") with side effects, there is an order dependence. Hence, we should probably either specify the order, or make it clear the order is arbitrary, as we do in various other places in the language. Caveat Side-Effector! **************************************************************** From: Steve Baird Sent: Wednesday, May 16, 2018 1:05 PM > I am happy to write up this refinement of this AI, if there is some > interest. There is indeed interest, at least from me. I like the "killing two birds with one stone" feature of this approach. One concern that crosses my mind is whether this somehow weakens the compile-time checking associated with case statements/expressions in situations where this new generality is not wanted. I have seen production code which includes something like if False then case X is ... end case; end if; where the compile-time checking is the sole reason for the case statement. After thinking about some plausible scenarios, I don't think this is a problem; still, it seemed worth mentioning in case someone sees an issue here. > Ranges would be permitted for scalar types, and then the normal > "membership" operation would be used I suppose this could be generalized to allow non-scalar subtypes if this seems desirable. subtype S is My_Type ... ; begin case My_Object is when S => **************************************************************** From: Tucker Taft Sent: Wednesday, May 16, 2018 1:41 PM > One concern that crosses my mind is whether this somehow weakens the > compile-time checking associated with case statements/expressions in > situations where this new generality is not wanted. > > I have seen production code which includes something like > if False then > case X is ... end case; > end if; > where the compile-time checking is the sole reason for the case > statement. > > After thinking about some plausible scenarios, I don't think this is a > problem; still, it seemed worth mentioning in case someone sees an > issue here. If the case choices are all static, the proposal will state that the usual rules apply *unless* the case expression is *also* static, in which case the only requirement is that exactly one alternative matches. I think that provides what you need. >> Ranges would be permitted for scalar types, and then the normal >> "membership" operation would be used > > I suppose this could be generalized to allow non-scalar subtypes if > this seems desirable. > > subtype S is My_Type ... ; > begin > case My_Object is > when S => Good point. Using subtype names makes perfect sense for non-scalar types as well, using membership semantics. One goal was to ensure that membership and case statements provide similar power, including for composite types. Subtypes names are permitted in membership tests so make sense here as well. The key thing that case statements will provide is the unambiguous selection of one alternative, whereas an "if ... elsif ... else .." statement would always be dependent on ordering of the if "alternatives," which seems less desirable in many situations. **************************************************************** From: Justin Squirek Sent: Wednesday, May 16, 2018 5:07 PM Is there any possible way to also incorporate some sort of optional syntactical enhancement? Take the string comparison example - if we some add the ability to list the left-hand part of a comparison such a thing could be achievable. For example: case is when X > 5 => ... when X = 5 => ... when X < 5 => ... end case; Could be: case for X is when > 5 => ... when = 5 => ... when < 5 => ... end case; **************************************************************** From: Tucker Taft Sent: Wednesday, May 16, 2018 5:38 PM I agree it could be a convenience, but this could be a nightmare to parse. I wouldn't recommend going this direction. This would add complexity to the language and the tools, without adding any fundamental power. **************************************************************** From: Justin Squirek Sent: Wednesday, May 16, 2018 5:40 PM What about the possibility of optimization? If X is an expression then it wouldn't have to be reevaluated in every case. **************************************************************** From: Jean-Pierre Rosen Sent: Wednesday, May 16, 2018 11:17 PM If you want to go this way, @ would be more appropriate: > For example: > case is > when X > 5 => ... > when X = 5 => ... > when X < 5 => ... > end case; > > Could be: > case X is > when @ > 5 => ... > when @ = 5 => ... > when @ < 5 => ... > end case; > ... **************************************************************** From: Yannick Moy Sent: Wednesday, May 16, 2018 10:21 AM >Given there is also some interest in having a case statement over composite >objects, I wonder if we could kill two birds with one stone. In particular, >we could relax the requirement that all choices in a case statement need to >be static, and instead require that, if the choices are *not* all static, >rather than requiring that they be non-overlapping, we require that exactly >one case alternative matches at run-time, raising Program_Error (or >Constraint_Error?) if not. I am very much in favor of this proposal for booleans, not so much for other types. First, there is a risk that people think the compiler ensures proper case distinction, when it's not the case anymore if one value in the case happens not to be static. Second, the most useful case distinction on non-boolean types is pattern matching, where you _introduce_ variable names (or use wildcards when you don't care) to match on a subset of the structure/value of the input. So just testing equality is totally inadequate here. **************************************************************** From: Justin Squirek Sent: Wednesday, May 16, 2018 11:50 PM Sounds like a reasonable restriction given that the goal isn't pattern matching as Tuck stated originally. **************************************************************** From: Randy Brukardt Sent: Thursday, May 17, 2018 12:28 AM > ... we could simply write: > > case True is > when X > 5 => ... > when X = 5 => ... > when X < 5 => ... > end case; Not if there is more than one True defined anywhere -- remember that a selecting_expression is a complete context. This is not purely an academic concern -- the Janus/Ada compiler has a type with literals (True, False, Kill) [it started out as a Boolean, and then we needed to add "Kill" for some reason]. In such a scenario, you'd have to write: case Boolean'(True) is when X > 5 => ... when X = 5 => ... when X < 5 => ... end case; which seems like one of the annoyances Ada is noted for. ;-) ====================== Steve Baird writes: > Tucker Taft wrote: ... > One concern that crosses my mind is whether this somehow weakens the > compile-time checking associated with case statements/expressions in > situations where this new generality is not wanted. > > I have seen production code which includes something like > if False then > case X is ... end case; > end if; > where the compile-time checking is the sole reason for the case > statement. > > After thinking about some plausible scenarios, I don't think this is a > problem; still, it seemed worth mentioning in case someone sees an > issue here. This is definitely the major concern I have. Current Ada programmers often depend on the static checking of case statements (the case Steve gives above is extreme, but more normally I use case checking to avoid detailed examination of updated code -- the compiler will tell me if I need to change a case statement). I'd hate to silently lose that property because of a mistake (a non-static object declaration, for instance). For instance, consider: At_Sign : Character := '@'; -- Forgot "constant". ... case Expr is when 'A' .. 'Z' => ... when At_Sign => ... -- (1) when '@' | '/' | '\' => ... -- (2) when others => ... end case; Currently, (1) is illegal because At_Sign is not static. If that is corrected by adding the missing "constant", then (2) is illegal because of the repeated '@'. However, consider what would happen with this given Tucker's exact idea. In this example as written, the case statement would become nonstatic, and the case statement would be always allowed -- the alternative check would be a dynamic check -- that would only fail if Expr = '@'. That individual example could easily be missed in testing, leaving a hazard in production code. (Yes, static analysis could reduce such a risk, but we don't want to add features that *require* static analysis -- that's my main objection to Contract_Cases, after all.) Existing Ada programmers in particular would be at risk of not noticing this problem, thinking that if the case compiled then it can't raise an exception (a rather useful property of case statements!). Because of this effect, I'd prefer that the dynamically checked case form was syntactically different than the form that is statically checked. (It doesn't have to be very significantly different.) ================= My other concern is that we not foreclose the possibility of doing static checking for at least some composite case statements in the future. The Raphael proposal that started this discussion (AI12-0214-1) included static checking for disjointness and in some cases for completeness. It's probably too complex to adopt now, but to completely throw away that possibility (and essentially force everyone to use add-on tools like CodePeer forever) seems like the wrong direction for Ada. (Usually we try to get more compile-time checking, not less!) ================= None of these are show-stoppers, but I do think that we need to very seriously consider them before charging ahead. Yannick wrote: > I am very much in favor of this proposal for booleans, not so much for >other types. I agree with his sentiment, but not necessarily his detailed reasons. :-) I think tuple case statements as defined in the latest AI12-0214-1 would be very useful, even without any insane un-Ada-like pattern matching. And I also think that the case Tucker suggests would often work for that, although the missing wildcards would be an issue. But losing static checking is going in the wrong direction for any usual case statement -- I feel better about that in the case of free-form Boolean expressions, as no one could expect overlap of arbitrary expressions to be detected at compile-time. Thus I think I prefer Tucker's earlier idea of "case is", meaning that the selecting_expression is implied to be the Boolean value True, the choices don't need to be static, and the runtime disjointness check is made. It would be hard to confuse these two forms, there'd be need to qualify True, and there's no problem expanding statically checked case statements in the future (especially to deal with tuples, which are trouble because they're never static with the current rules). Tucker wrote: > I am happy to write up this refinement of this AI, if there is some > interest. I already wrote up the "case is" version as part of our scope (I'll be posting all of that as soon as Jeff & Steve sign off on it). A third alternative would surely be in scope, so feel free to write it up if you want to persue it. You're the one with the current homework item for AI12-0214-1 -- as you are the one that voted against putting it on hold (no good deed goes unpunished around here) -- so if you don't want to update my sloppily split version, you can write a new alternative instead. Or just endorse my AI12-0214-2. Or just do other homework instead. :-) **************************************************************** From: Randy Brukardt Sent: Thursday, May 17, 2018 1:42 AM ... > I already wrote up the "case is" version as part of our scope (I'll be > posting all of that as soon as Jeff & Steve sign off on it). A third > alternative would surely be in scope, so feel free to write it up if > you want to pursue it. You're the one with the current homework item > for > AI12-0214-1 -- as you are the one that voted against putting it on > hold (no good deed goes unpunished around here) -- so if you don't > want to update my sloppily split version, you can write a new > alternative instead. Or just endorse my AI12-0214-2. Or just do other > homework instead. :-) BTW, I'd recommend classifying AI12-0214-2 as "simple" (it's five new/modified paragraphs for case expressions) and putting it after AI12-0237-1 in the priority list. AI12-0214-1 is not simple by any interpretation. I don't think AI12-0214-3 (Tucker's latest idea) would make the cut of "simple" either, although I'd have to see it to be sure -- it would require substantial syntax changes ("discrete_choice_list") would have to be replaced in case statements/expressions since it is used elsewhere -- and I surely hope no one is intending to extend variants!!) and presumably a number of wording changes to support those changes. Ergo, we're much more likely to discuss AI12-0214-2 if that's our choice than the alternatives. But I suppose we have to choose one first... **************************************************************** From: Raphael Amaird Sent: Thursday, May 17, 2018 3:48 AM I personally would like to understand what your proposal (Tuck's) brings to the table that the full version of 0214 doesn't. It looks much less powerful to me, adding also the risk of silently loosing static checks in the process, while not bringing much more to the table. I don't remember hearing a proper rebuttal of 0214, besides "it's complicated to implement". In my mind, this should come before yet another proposal. I know that this argument doesn't appeal to ARG members (and I don't care honestly), but case pattern matching is a tried and battle tested abstraction that has proven very useful in countless because of the *static* guarantees it provides, which should be something very important to us for Ada. Instead I see another proposal that is much more dynamic in nature, much less useful in my opinion, without even properly discussing the previous one. I'm a bit disappointed to be honest. **************************************************************** From: Edward Fish Sent: Thursday, May 17, 2018 12:46 PM > Given there is also some interest in having a case statement over > composite objects, I wonder if we could kill two birds with one stone. > In particular, we could relax the requirement that all choices in a > case statement need to be static, and instead require that, if the > choices are *not* all static, rather than requiring that they be > non-overlapping, we require that exactly one case alternative matches > at run-time, raising Program_Error (or Constraint_Error?) if not. > Then for this "contract-case"-inspired case statement, we could > simply write: > > case True is > when X > 5 => ... > when X = 5 => ... > when X < 5 => ... > end case; Honestly what we would want for something like this is a construct that was integrated with functions; which doesn't really work with Ada because we can't tie functions in Ada together like we can in mathematics, if we could then we could say something like: case X, 5 of when ">" => --... when "<" => --... when "=" => --... end case; which is a *LOT* nicer, IMO; but simply can't be generalized without the sort of tight relationship that ">"/"="/"<" have in mathematics. (The above /would/ fit with Ada's notion of CASE in mathematics: statically known coverage of all alternatives.) I really don't want to have CASE's nice semantics (namely statically-known and covered branches) to be violated. **************************************************************** From: Tucker Taft Sent: Thursday, May 17, 2018 1:17 PM > Instead I see another proposal that is much more dynamic in nature, much > less useful in my opinion, without even properly discussing the previous > one. > > I'm a bit disappointed to be honest. Sorry to not link this to 0214. I agree that is more powerful, and I should have made it clear that this was really just the germ of an idea of how to link the current discrete-only case statement to a more general kind of case statement that supports the Contract-Case-like construct as well as composite types. I agree once we get into composite types, there are many more interesting kinds of pattern matching that we can (and should) support. But I would hope that we *at least* support what is already supported in membership tests. I think it is important that there be some degree of symmetry between case statements and membership tests, so that is what I was trying to accomplish. So I should have described this as a way to link the various ideas together, and not intended to be a replacement for the more advanced kinds of composite case pattern matching. **************************************************************** From: Randy Brukardt Sent: Thursday, May 17, 2018 6:50 PM > I personally would like to understand what your proposal (Tuck's) > brings to the table that the full version of 0214 doesn't. > > It looks much less powerful to me, adding also the risk of silently > loosing static checks in the process, while not bringing much more to > the table. > > I don't remember hearing a proper rebuttal of 0214, besides "it's > complicated to implement". This is an irrelevant question. The ARG is in the business of solving problems, not of "rebutting" proposals. The full version of AI12-0214-1 never garnered much support, hardly anyone voted for the "case pattern matching" proposal in the priority (I note that even you didn't vote for it at that time). Nor did anyone make a case to vote against indefinitely deferring it (a vote that you didn't even take part in). So it clearly was judged not important enough for this iteration, but no other final determination has been made. As for the remaining part (the homework that you never did so I had to do it myself), we've never discussed that option by itself at a meeting or here, and it remains alive as an option. > In my mind, this should come before yet another proposal. That is not how the ARG works. Proposals before the ARG are designed to solve problems. At any time, someone can propose an alternative solution to solving the problem; it doesn't matter what has happened or not happened with the previous proposals. Indeed, we encourage additional proposals because it's quite possible that someone may have an idea that is simpler or fits into the language better. A simple idea that addresses 90% of a problem might be a better fit for Ada 2020 than a complex solution that addresses 100% of the problem. A case in point: the AI12-0240-1 "owned pointer" proposal. It would be hard to "rebut" such a proposal -- it is so complex that I cannot determine answers to my questions/concerns about it. It very well could be perfect, but it is very difficult to tell. In any case, I'm working on a much simpler alternative proposal that concentrates on a critical part of the problem (implementing containers and other ADTs in a compile-time safe way for parallelism) and doesn't try to solve every angle. I have no idea how my version will be received, but I feel I have to try given the time limitations we have for Ada 2020. The same dynamic is going on here. > I know that this argument doesn't appeal to ARG members (and I don't > care honestly), but case pattern matching is a tried and battle tested > abstraction that has proven very useful in countless because of the > *static* guarantees it provides, which should be something very > important to us for Ada. If you didn't use the words "pattern matching", I think everyone would agree with you. The static guarantees of a case statement are critically important. OTOH, "pattern matching" is a solution in search of a problem. There are a variety of tools, both in compilers and outside of them (i.e. CodePeer) that can prove absence of potential exceptions. Dealing with a very limited case that way rather than looking at the entire expanse of code does not seem to be of much value. (Particularly when it involves the most verbose of the Ada statement kinds!) > Instead I see another proposal that is much more dynamic in nature, > much less useful in my opinion, without even properly discussing the > previous one. My proposal, of which Tucker's proposal is intended to be a unification of, is intended to solve a very different problem: the case of arbitrary conditions that are disjoint. Whether or not this really is a case expression is hard to say: for me, it is about halfway in between a case and an if. I originally proposed it to be a new kind of thing because I didn't think it shared enough commonality with case (which to me can only have static choices). One can think of the "alternative expression" (still searching for a good name) as an if statement with an assertion of disjointness in the conditions. That assertion isn't of much use in Ada (if anything, it is a tripping hazard), but it can be of a lot of use to human readers of code as well as proof tools. I do agree with you in one sense: the "alternative expression" has to be syntactically different than a case statement: a case statement has a static check for disjointness, and that cannot be allowed to turn into a potentially dangerous dynamic check by a mistake in declaration or other maintenance. Anyway, a case expression, even your overblown "pattern matching" case expression, cannot handle many cases that the "alternative expression" can. Since the primary purpose is to provide a better way to write postconditions than Contract_Cases (which does not work well without the static proving capabilities), let's look at one example: procedure Incr (Value : in out Integer; Limit : in Integer) with Post => (case is when Value'Old < Limit => Value = Value'Old + 1, when Value'Old = Limit => Value = Value'Old, when Value'Old > Limit => raise Program_Error); You can't write this with any form of statically checked case expression, as Limit is not static. One could use an if expression, but then you've lost the useful information that only one of the conditions can be true in any given execution. (That matters more when the expressions get more complex.) Tucker seems to have some other issue in mind as well, which I don't understand at all -- see my reply to him on that one. IMHO, there is value to the reduced version of AI12-0214-1, but it simply isn't important enough to pursue in Ada 2020 -- with the end of work rapidly approaching, we have to drop many good ideas if we have any chance of getting done on time. > I'm a bit disappointed to be honest. The ARG is trying to solve the most important problems with the least impact on users and implementations, and as a corollary the least change in the Standard. The search for better solutions surely doesn't stop just because a single solution has been proffered. I'm a bit disappointed that you, as an ARG member, has lost sight of the real goal in favor of pushing a grandiose solution to a relatively minor problem. (I'm also disappointed in myself for managing to spend an entire hour crafting this answer. :-) **************************************************************** From: Randy Brukardt Sent: Thursday, May 17, 2018 7:06 PM ... > But I would hope that we *at least* support what is already supported in > membership tests. I think it is important that there be some degree of > symmetry between case statements and membership tests, so that is what > I was trying to accomplish. I have never understood this at all. In my view, the critical difference between a case expression and an if expression is the static guarantees of disjointness and completeness. I almost never use a case expression/statement unless I want one or both of those guarantees. (I realize that case originated as a way to get jump tables into high-level languages, but that sort of implementation concern is way down the list for case usage these days.) OTOH, a membership test is now and always has been a dynamic test. It makes no kind of guarantee at all. As such, it is completely incompatible with the primary goal of the case statement and I would never think about using them together anymore than I would think about using a random number generator in a case choice. This is in part why I believe that it is critical that the "alternative expression" be syntactically distinct from a "case expression". A runtime disjointness check is a tripping hazard; it directly provides no value. The value comes from the assertion (mainly to other tools) that the choices are disjoint, and hopefully that those other tools can prove the disjointness check isn't needed. Having a situation where the disjointness check can change from the expected static check to a dynamic hazard without warning to the programmer seems to be the wrong direction for things to go. So I see trying to integrate these things (the case expression/membership test/alternative expression) as actively harmful. Obviously, you are seeing this very differently and I am wondering why. The effective elimination of static checks from case statements (one could not assume that they are being made if your proposal was adopted, since no reader can tell reliably whether a specific entity is static or not, outside of a few special cases) seems to go against everything that you tried to do in Ada 95 (push as many checks as possible from runtime to compile-time). I can't believe that you haven't considered that effect, so can you explain your thinking?? **************************************************************** From: Randy Brukardt Sent: Thursday, May 17, 2018 7:18 PM ... > Honestly what we would want for something like this is a construct > that was integrated with functions; which doesn't really work with Ada > because we can't tie functions in Ada together like we can in > mathematics, if we could then we could say something like: > > case X, 5 of > when ">" => --... > when "<" => --... > when "=" => --... > end case; > > which is a *LOT* nicer, IMO; but simply can't be generalized without > the sort of tight relationship that ">"/"="/"<" have in mathematics. > (The above /would/ fit with Ada's notion of CASE in mathematics: > statically known coverage of all > alternatives.) I really don't want to have CASE's nice semantics > (namely statically-known and covered branches) to be violated. What we need is a cross between a case and if statement that is actually neither. I don't think it is productive to think of the "alternative statement" as a case statement -- indeed, I started this proposal with a different keyword for that very reason. In any case, the above sort of thing would only work in very limited circumstances. The sorts of real-world examples of Contract_Cases that Tucker provided couldn't be written in this form; they need a alternative expression in general, some specific cases could be written with an extended composite case expression (but that is very complex to define, since to get ranges into it -- which seems like a requirement to me -- we can't use the aggregate syntax/rules directly; and the rules for determining overlap are also quite complex when using <> as "others" for individual components). The expressions involved in these examples are completely arbitrary, possibly involving functions (common in good abstractions), multiple parameters, and more. Any sort of static overlap checks require full SPARK-like proving technology, and that's obvious out-of-bounds for Ada the language. The inability to useful static checks is why I don't believe that they should be thought of (or be syntactically) the same as a case. The above seems to only work for predefined operators (I can't see any way to make static non-overlap checks with user-defined functions), and thus would be limited to very few cases. I'd expect compilers to make such checks statically in such cases that they're equipped to figure out -- probably giving a warning if there is a problem as in any case where a compiler can figure out that a check is going to fail. **************************************************************** From: Tucker Taft Sent: Thursday, May 17, 2018 9:06 PM > ... >> But I would hope that we *at least* support what is already supported in >> membership tests. I think it is important that there be some degree of >> symmetry between case statements and membership tests, so that is >> what I was trying to accomplish. > > I have never understood this at all. In my view, the critical > difference between a case expression and an if expression is the > static guarantees of disjointness and completeness. I almost never use > a case expression/statement unless I want one or both of those > guarantees. (I realize that case originated as a way to get jump > tables into high-level languages, but that sort of implementation > concern is way down the list for case usage these days.) > > OTOH, a membership test is now and always has been a dynamic test. It > makes no kind of guarantee at all. As such, it is completely > incompatible with the primary goal of the case statement and I would > never think about using them together anymore than I would think about > using a random number generator in a case choice. ... One of my most frequent code "restructuring"s is to take an if/elsif/elsif/else construct and turn it into a case statement. These original "if" statements are generally a series of comparisons and membership tests. Turning them into a case statement takes advantage of the fact that the choices in an Ada case statement are essentially the same as what you can use in the right-hand-side of a membership test or an equality test. So it seems natural to me that when you generalize membership tests, you would generalize case statements in the same way. The nice thing about a case statement is that you know order doesn't matter (except for the "when others"), whereas with an if/elsif/... you can get yourself tied in knots trying to understand why a particular "if" alternative is never reached, because you logically have to negate all of the earlier "if" conditions to understand why. As far as the (boolean) "case selection" statement, or whatever we want to call it, I understand the very nice feature of static disjointness check of discrete case statements, and wouldn't want to interfere with that. I guess I was attracted to two things, one was not needing to invent a new syntax, and the other was to allow case statements over things like general composite types that are *never* static. When it became clear that the property of having a unique alternative that "matched" could be a run-time check rather than a compile-time check, a bell went off in my head. Composite case statements and this "case selection" statements would want effectively the same rule -- exactly one alternative should be selected -- it shouldn't be order dependent. In fact, pattern matching might *prefer* an ordered construct, so each pattern could get more and more generic, ending with "when others," so perhaps this is an unwise "unification" in any case... I think at this point I'll just withdraw my proposal, and go back to the "case is" syntax for the boolean case selection statement. I would still like to see a composite case statement, with a minimum allowing a simple set of equality and membership tests, and better supporting the use of things like (<>, "red") as choice patterns. And then I would probably expect to be able to use those same sort of patterns in membership tests! ;-) **************************************************************** From: Randy Brukardt Sent: Thursday, May 17, 2018 9:36 PM ... > One of my most frequent code "restructuring"s is to take an if/elsif/elsif/else > construct and turn it into a case statement. I don't know if I've ever done that - maybe very rarely with equality on some enumeration type - probably because I wanted to avoid repeating evaluation (and writing!) a complex expression. I certainly never thought that there was any sort of equivalence - I've always thought that I was replacing one thing by something different and trying to preserve the semantics. (And I almost never write a membership, but I suppose that's partly because I never implemented any of the expansions.) Anyway, thanks for the explanation. ... > the other was to allow case > statements over things like general composite types that are > *never* static. I don't see an issue here, so long as the composite values themselves are made up of static components (along with "null") and "others" choices (represented by <>s). The coverage rules are essentially the same as for discrete types. (On top of which, we should allow general composite types to have static expressions, but I know that never got enough traction.) ... > In fact, pattern matching might *prefer* an ordered construct, so each > pattern could get more and more generic, ending with "when others," so > perhaps this is an unwise "unification" in any case... I detest calling composite case coverage "pattern matching"; it is the same thing as discrete case coverage with extended rules. To me, "pattern matching" involves runtime things, and those shouldn't be in case statements. I agree that the <>s should go at the end, but that is a rule very similar to the one for "others" - it provides a partial order at most. The big problem with proper composite case coverage is that we need to include ranges if we want to support numeric types sanely, and that means that we have to extend the aggregate syntax somehow. (Or we have to make ranges into first-class expression components, which would require a lot of changes elsewhere in the language.) It's probably too late to make such changes for Ada 2020 - but I wouldn't want to forclose the possibility for the future. **************************************************************** From: Yannick Moy Sent: Friday, May 18, 2018 2:03 AM > I detest calling composite case coverage "pattern matching"; it is the same > thing as discrete case coverage with extended rules. To me, "pattern > matching" involves runtime things, and those shouldn't be in case > statements. I think you're confusing "pattern matching" as used for regular expressions with "pattern matching" as used in functional languages. The proposal of Raphael was to adapt the latter to Ada, and there would be a completely static check for completeness of the set of "patterns" proposed (although not disjointness, as Tuck explained, because we want in general to start with more specific patterns and have latter more general ones). Can you clarify why you think this is "dynamic"? **************************************************************** From: Raphael Amiard Sent: Friday, May 18, 2018 4:44 AM >This is an irrelevant question. The ARG is in the business of solving >problems, not of "rebutting" proposals. What problem exactly was the ARG solving when they added object oriented programming to the language ? "Solving problems" is extremely subjective in language design. >The full version of AI12-0214-1 never garnered much support, hardly anyone >voted for the "case pattern matching" proposal in the priority (I note that >even you didn't vote for it at that time). Yes. That's not in itself a very good indicator of how useful this proposal would be, more about how unfamiliar it is to ARG members. That's just my opinion, but let's not pretend the support it got is a clear indicator of its absolute value. >Nor did anyone make a case to >vote against indefinitely deferring it (a vote that you didn't even take >part in). So it clearly was judged not important enough for this iteration, >but no other final determination has been made. Yes, I'm sorry about this actually! I have been slacking on homework because, unfortunately, the ARG is just one of many of my professional responsibilities. This is not an excuse, though it has been hard to combine everything for me lately. Also, and I'm not saying this to justify myself, but working on proposals for features that don't seem to gather any interest or approbation can get very demoralizing. >That is not how the ARG works. Proposals before the ARG are designed to >solve problems. At any time, someone can propose an alternative solution to >solving the problem; it doesn't matter what has happened or not happened >with the previous proposals. Indeed, we encourage additional proposals >because it's quite possible that someone may have an idea that is simpler or >fits into the language better. If that is the case, it should be indicated *how* it compares to the other proposal IMHO, and how it fits the language better. That would in general be very helpful for people to understand how things work. >A simple idea that addresses 90% of a problem >might be a better fit for Ada 2020 than a complex solution that addresses >100% of the problem. That's just your opinion. I actually quite strongly disagree with this mentality. >A case in point: the AI12-0240-1 "owned pointer" proposal. It would be hard >to "rebut" such a proposal -- it is so complex that I cannot determine >answers to my questions/concerns about it. It very well could be perfect, >but it is very difficult to tell. In any case, I'm working on a much simpler >alternative proposal that concentrates on a critical part of the problem >(implementing containers and other ADTs in a compile-time safe way for >parallelism) and doesn't try to solve every angle. I have no idea how my >version will be received, but I feel I have to try given the time >limitations we have for Ada 2020. > >The same dynamic is going on here. This is a strawman as far as I can tell. There is absolutely no comparison in complexity between the pattern matching proposal and the owned pointer one. >>I know that this argument doesn't appeal to ARG members (and I don't >>care honestly), but case pattern matching is a tried and battle tested >>abstraction that has proven very useful in countless because of the >>*static* guarantees it provides, which should be something very >>important to us for Ada. >If you didn't use the words "pattern matching", I think everyone would agree >with you. The static guarantees of a case statement are critically >important. OTOH, "pattern matching" is a solution in search of a problem. Again, please don't pretend this kind of statements are objective truth. Pattern matching is a solution solving many problems, not the least of it being the ability to statically guarantee access to variant fields. There is a lot of literature on it, and if you are really interested you need only search. There are many languages today that implement it, so experimenting with it is not hard. >There are a variety of tools, both in compilers and outside of them (i.e. >CodePeer) that can prove absence of potential exceptions. Dealing with a >very limited case that way rather than looking at the entire expanse of code >does not seem to be of much value. (Particularly when it involves the most >verbose of the Ada statement kinds!) I'm sorry but this is like saying "there is no value to the static guarantees of Ada, because you have static analyzers for C". This is a very caricatural statement in my opinion. If you can add static guarantees in the language, it *at least* warrants some level of attention in my opinion, not outright dismissal, which has pretty much been the case here. >My proposal, of which Tucker's proposal is intended to be a unification of, >is intended to solve a very different problem: the case of arbitrary >conditions that are disjoint. Whether or not this really is a case >expression is hard to say: for me, it is about halfway in between a case and >an if. I originally proposed it to be a new kind of thing because I didn't >think it shared enough commonality with case (which to me can only have >static choices). Yes, I understand that it is quite different, and that it is solving a different problem. I understand that a bit better now. Actually, the two proposals are orthogonal enough that they might be combined. They *can* however serve similar purposes in real life use cases, which is why I wrote what I wrote. >I do agree with you in one sense: the "alternative expression" has to be >syntactically different than a case statement: a case statement has a static >check for disjointness, and that cannot be allowed to turn into a >potentially dangerous dynamic check by a mistake in declaration or other >maintenance. Yes >Anyway, a case expression, even your overblown "pattern matching" case >expression, It would be very appreciable in my opinion if you refrained from this kind of inflammatory and subjective qualifiers. What you consider overblown, many people consider a basic feature of their day-to-day programming language. I personally think the work we do on parallel programming is "overblown", in that its complexity is much higher than the value it is going to bring to programmers. I try to refrain to commonly qualify it as such, if only by respect for people who are working on it. >cannot handle many cases that the "alternative expression" can. Yes, I'm aware of that. Many a language provide both (Scala for example), because they never tried to have a case statement that is as statically safe as Ada (no completeness check). >IMHO, there is value to the reduced version of AI12-0214-1, but it simply >isn't important enough to pursue in Ada 2020 -- with the end of work rapidly >approaching, we have to drop many good ideas if we have any chance of >getting done on time. I understand that we should have priorities. I'm allowed even if the result is pretty useless in the end, to disagree on those priorities pretty strongly :) >The ARG is trying to solve the most important problems with the least impact >on users and implementations, and as a corollary the least change in the >Standard. The search for better solutions surely doesn't stop just because a >single solution has been proffered. I'm a bit disappointed that you, as an >ARG member, has lost sight of the real goal in favor of pushing a grandiose >solution to a relatively minor problem. (I'm also disappointed in myself for >managing to spend an entire hour crafting this answer. :-) I'm repeating myself at this stage, but there is almost no originality in my grandiose proposal. It is a pretty simple adaptation of a very common construct in functional programming. The mechanics, as well as the problems it solves, are pretty well understood at this stage. In any case thank you for taking the time to write a detailed answer. I guess that something we can do is disagree with a lot of words :-) *************************************************************** From: Raphael Amiard Sent: Friday, May 18, 2018 5:00 AM >I note that >even you didn't vote for it at that time Just want to answer that, I don't think that's True. Or if it is, I must have been properly demoralized by the general reaction to this AI and did not even want to bother. **************************************************************** From: Randy Brukardt Sent: Friday, May 18, 2018 5:53 PM >>I detest calling composite case coverage "pattern matching"; it is the same >>thing as discrete case coverage with extended rules. To me, "pattern >>matching" involves runtime things, and those shouldn't be in case >>statements. >I think you're confusing "pattern matching" as used for regular >expressions >with "pattern matching" as used in functional languages. I don't think I was confusing them so much as looking at them both as views of the same thing (a dynamic process). Every functional language that I hear about anyone using is primarily dynamic; there's nothing static in them in the sense of Ada compilation. >The proposal of Raphael was to adapt the latter to Ada, and there would >be a completely static check for completeness of the set of "patterns" proposed Yes, of course. But this is not "pattern matching" and there aren't really "patterns" here anyway (just choices representing sets of values); Ada already has terms for this ("choice" and "covers" in particular) and I'd much prefer to stay as much as possible with the existing terminology. Copying stuff from other languages makes sense in a vacuum, but Ada case statements are not a vacuum! >(although not disjointness, as Tuck explained, because we want in >general to start with more specific patterns and have latter more general >ones). I surely hope that we are enforcing disjointness. Including some wild-cards ("others" is such a wild-card in existing Ada) does not make the disjointness check less important. I realize this again is more terminology than semantics. More specifically, a wild-card represents all of the values not used in other, similar choices. Use of wild-cards prevent completeness checks, but you still need disjointness (including non-overlap of choices that don't use wildcards, and prohibition of multiple wildcards if the other components overlap, etc.) >Can you clarify why you think this is "dynamic"? "Pattern matching" implies (to me at least) a sequential matching of the selecting_expression to each pattern -- in large part because the patterns are, in general, too complex to do anything else. That, to me at least, is the wrong model for case statements. One wants the idea that comes to mind is a direct execution of the appropriate alternative. And given that Ada has terms for "choices" (not "patterns"), and "covers" (not "matching"), I'd rather skip the connotations. If the goal of using such terms is to attract functional programmers to Ada, I don't think there is much point in attempting to do that. People don't switch programming languages just because one is "better" and offers the same things they are used to. **************************************************************** From: Randy Brukardt Sent: Friday, May 18, 2018 6:00 PM >>I note that >>even you didn't vote for it at that time >Just want to answer that, I don't think that's True. Or if it is, must >have been properly demoralized by the general reaction to this AI and >did not even want to bother. I looked up your ballot before I wrote that: I was going to say something like "even you only rated it as the nth most important AI" and was surprised to see that it wasn't even on it -- only other people voted for it. So it is True. I had the same feeling about several AIs that I personally feel strongly about but didn't vote for -- with only ten votes, I had to eliminate 6 AIs from the list and ones I didn't think anyone else was going for were an obvious choice to drop. But that was part of the point of the exercise -- impose some scarcity on the group to see what REALLY matters. **************************************************************** From: Randy Brukardt Sent: Friday, May 18, 2018 6:55 PM I'm going to try to respond to just a couple of thoughts, it's probably not worth either of our time to get into a lengthy discussion... >>This is an irrelevant question. The ARG is in the business of solving >>problems, not of "rebutting" proposals. > >What problem exactly was the ARG solving when they added object >oriented programming to the language ? OOP was added by the Ada 9x term, and they were working under different rules. However, I'd answer that by saying problems that need dynamic dispatching (safe dynamic calls). Why they gave us *two* ways to do that is not clear. >"Solving problems" is extremely subjective in language design. True enough. We adopted that mantra because we were getting (and continue to get) feature proposals for which it is very unclear what the purpose is. Sometimes, there is already a way to solve the actual program, or a much simpler fix will work. Without some idea of what the purpose is in actual use, it's impossible to tell. I also could have said something about building consensus. Proposals are out until the group agrees that they're in. And I've had proposals killed for reasons as silly as not liking the proposed syntax. (That particular proposal is back on our agenda 15 years later, proposed this time by an AdaCore group. We'll see what happens this time.) There's nothing anywhere about careful, logical consideration of anything. Sorry to disappoint you on that. :-) ... >>That is not how the ARG works. Proposals before the ARG are designed >>to solve problems. At any time, someone can propose an alternative >>solution to >>solving the problem; it doesn't matter what has happened or not >>happened with the previous proposals. Indeed, we encourage additional >>proposals because it's quite possible that someone may have an idea >>that is simpler or >>fits into the language better. >If that is the case, it should be indicated *how* it compares to the >other proposal IMHO, and how it fits the language better. That would in >general be very helpful for people to understand how things work. Yes, that would have been required of an actual AI. In this case, Tucker floated a trial ballon, and it quickly got leaks and was grounded before a formal proposal happened. >>A simple idea that addresses 90% of a problem might be a better fit >>for Ada 2020 than a complex solution that addresses 100% of the >>problem. > >That's just your opinion. I actually quite strongly disagree with this >mentality. Fair enough. But in rejecting the above, you are essentially saying that the most complex solution is always the best (presuming that it does actually solve the problem). And I rather doubt that you believe *that*. >>A case in point: the AI12-0240-1 "owned pointer" proposal. It would be hard >>to "rebut" such a proposal -- it is so complex that I cannot determine >>answers to my questions/concerns about it. It very well could be >>perfect, but it is very difficult to tell. In any case, I'm working on a much >>simpler alternative proposal that concentrates on a critical part of the >>problem (implementing containers and other ADTs in a compile-time safe way for >>parallelism) and doesn't try to solve every angle. I have no idea how >>my version will be received, but I feel I have to try given the time >>limitations we have for Ada 2020. >> >>The same dynamic is going on here. >This is a strawman as far as I can tell. There is absolutely no >comparison in complexity between the pattern matching proposal and the >owned pointer one. Your original AI12-0214-1, with it's nested composite matching, automatic dereferences, and auto-renaming of subparts, would require far more wording than the current case statements. (I'd guess around five pages of wording.) The implementation complexity is off the charts, in part because the aggregate complexity and the code generation complexity cross-cut in opposition to each other. To do a good job, one will need many special cases (an if statement implementation should be a last resort for cases with more than a handful of alternatives). It's probably less complex than AI12-0240-1, but the difference in complexity between it and it alternatives are about the same order of magnitude that the AI12-0240-1 and my alternative (which is simpler, but still is plenty complex). >>There are a variety of tools, both in compilers and outside of them (i.e. >>CodePeer) that can prove absence of potential exceptions. Dealing with >>a very limited case that way rather than looking at the entire expanse of code >>does not seem to be of much value. (Particularly when it involves the >>most verbose of the Ada statement kinds!) >I'm sorry but this is like saying "there is no value to the static guarantees >of Ada, because you have static analyzers for C". This is a very caricatural >statement in my opinion. If you can add static guarantees in the language, it >*at least* warrants some level of attention in my opinion, not outright >dismissal, which has pretty much been the case here. You're talking about static guarantees on variant references. But OOP code doesn't even use variants, so they're not that common in new code. And to get those guarantees, you have to introduce large complex amounts of code (the case statement). Doing that in existing code is impractical. I'd be very interested in expanding static checks if it didn't require so much rewriting (essentially, a different programming style). To take an specific example, the middle pass of the Janus/Ada extensively uses variants (it was original written in the early 1980s, there was nothing else). Most of the code assumes that the tree, symboltable record, or whatever has the expected form. We used to have some explicit checks for that, but they didn't buy anything more than the built-in Ada variant checks. For instance, the entire tree => IR phase assumes the tree is bound, resolved, and folded -- if that's not true, a variant check will fail somewhere. Adding code to statically guarantee that wouldn't buy much, but it would add a huge amount of code (particularly in dealing with the failure cases). It wouldn't be practical to do that. Besides, the bug is elsewhere in such cases (passing in a bad tree usually means that some previous phase was omitted altogether or there is a bug in some other phase) -- it would be better to detect the bug there. There's always some program on which a proposal will be valuable. It's hard for me to imagine that there are enough such programs to justify the six months of implementation work. >>Anyway, a case expression, even your overblown "pattern matching" case >>expression, >It would be very appreciable in my opinion if you refrained from this >kind of inflammatory and subjective qualifiers. Sorry. >What you consider overblown, >many people consider a basic feature of their day-to-day programming >language. Many people voted for , too. Doesn't mean he's good. :-) Same applies here. >I personally think the work we do on parallel programming is >"overblown", in that its complexity is much higher than the value it is >going to bring to programmers. I try to refrain to commonly qualify it >as such, if only by respect for people who are working on it. Interesting. Aren't you the guy who said something like: "If you can add static guarantees in the language, it *at least* warrants some level of attention in my opinion". And the whole point (IMHO) of the parallel work is to be able to add statically checked safe parallelism. Unlike variants, it is essentially impossible to do runtime race condition checking. And it's beyond the capabilities of normal programmers to recognize such cases. Conservative static checks are pretty much the only way to get the needed safety. If we can't get the static checks, then I definitely agree with you. Brad's libraries are good enough for unchecked parallel execution (possibility with a bit of syntactic sugar), and they'd be many times easier to define. **************************************************************** From: Jean-Pierre Rosen Sent: Friday, May 18, 2018 8:51 AM Here is another possibility for extending the case statement. It is mostly the case statement from the CHILL language (CCITT High Level Language). This variant is intended to ease the writing of state automatons. Syntax: case is when is => ... end case; All usual rules for case statements apply, only that you specify a list of variables, and for each path a matching list of values. The path is chosen if all variable values match. case I, J is when 1..10, 1..10 => -- both indices in top left of matrix when others => ... end case; The same can be achieved with nested case statement, with lots of duplications and less readable structure. ---- Improvement: Alternatively, could be a record variable whose components are all discrete, or an array of static size and discrete components. The would be required to have the same number of choices as the number of components of the record/array ---- Any interest? **************************************************************** From: Randy Brukardt Sent: Friday, May 18, 2018 6:07 PM > case I, J is > when 1..10, 1..10 => -- both indices in top left of matrix > when others => ... > end case; > > The same can be achieved with nested case statement, with lots of > duplications and less readable structure. I like this better than AI12-0214-1, as the extension of an existing implementation to support it is much more obvious. It's definitely less powerful (no embedded wildcards) but also a lot easier to describe (because there's no embedded wildcards :-). It doesn't have to deal with extra components (just don't write them in the expression) and variants and all of that mess. Probably the real downside for composites is the need to repeat the prefix: case .Height, .Width is Might be worth it for the other advantages. ****************************************************************