Version 1.7 of ai12s/ai12-0214-2.txt

Unformatted version of ai12s/ai12-0214-2.txt version 1.7
Other versions for file ai12s/ai12-0214-2.txt

!standard 4.5.7(5/3)          19-07-05 AI12-0214-2/03
!standard 4.5.7(6/3)
!standard 4.5.7(14/3)
!standard 4.5.7(21/3)
!standard 4.9(12.1/3)
!standard 4.9(32.5/3)
!standard 5.4(2/3)
!standard 5.4(3)
!standard 5.4(11/3)
!standard 5.4(14/3)
!class Amendment 18-05-16
!status Amendment 1-2012 19-07-05
!status ARG Approved 10-0-0 19-06-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 without a selecting_expression 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 case expression without a selecting_expression consists of a number of alternatives guarded by Boolean conditions. The conditions should be such that only one is true for any execution of the 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 case statements without a selecting_expression by changing dependent_expressions to 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 choice_condition_list => dependent_expression
choice_condition ::= choice_expression
choice_condition_list ::= choice_condition {'|' choice_condition}
[Editor's note: We need "choice_condition" to avoid syntax ambiguity with memberships]
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_conditions are evaluated. If exactly one choice_condition 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_condition is True, or multiple choice_conditions are True), Program_Error is raised.
AARM Ramification: This is not a check! It cannot 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_conditions, }and dependent_expressions are static expressions{. In addition, for a case_expression without a selecting_expression, exactly one of the choice_conditions is True};
Add after 4.9(32.5/3):
* a dependent_expression of a case_expression whose associated choice_conditions are all static and all of whose values equal 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 choice_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_conditions are evaluated. If exactly one choice_condition is True, the sequence_of_statements of the conditional_case_statement_alternative containing this choice_condition is executed. Otherwise (no choice_condition is True, or multiple choice_conditions are True), Program_Error is raised.
AARM Ramification: This is not a check! It cannot be suppressed as we would not know what statement to execute 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);
!corrigendum 4.5.7(5/3)
Replace the paragraph:
case_expression ::= 
   case selecting_expression is
   case_expression_alternative {,
   case_expression_alternative}
by:
case_expression ::= 
   case selecting_expression is
   case_expression_alternative {,
   case_expression_alternative}
 | case is
   conditional_case_expression_alternative {,
   conditional_case_expression_alternative}
!corrigendum 4.5.7(6/3)
Insert after the paragraph:
case_expression_alternative ::= 
   when discrete_choice_list =>
      dependent_expression
the new paragraphs:
conditional_case_expression_alternative ::= 
   when choice_condition_list =>
      dependent_expression
choice_condition ::= choice_expression
choice_condition_list ::= choice_condition {'|' choice_condition}
!corrigendum 4.5.7(14/3)
Replace the paragraph:
A condition is expected to be of any boolean type.
by:
A condition is expected to be of any boolean type. A choice_condition is expected to be of type Boolean.
!corrigendum 4.5.7(21/3)
Replace the paragraph:
For the evaluation of a case_expression, the selecting_expression is first evaluated. If the value of the selecting_expression is covered by the discrete_choice_list of some case_expression_alternative, then the dependent_expression of the case_expression_alternative is evaluated, converted to the type of the case_expression, and the resulting value 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.
by:
For the evaluation of a case_expression with a selecting_expression, the selecting_expression is first evaluated. If the value of the selecting_expression is covered by the discrete_choice_list of some case_expression_alternative, then the dependent_expression of the case_expression_alternative is evaluated, converted to the type of the case_expression, and the resulting value 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.
For the evaluation of a case_expression without a selecting_expression, all of the choice_conditions are evaluated. If exactly one choice_condition 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_condition is True, or multiple choice_conditions are True), Program_Error is raised.
!corrigendum 4.9(12.1/3)
Replace the paragraph:
by:
!corrigendum 4.9(32.5/3)
Insert after the paragraph:
the new paragraph:
!corrigendum 5.4(2/3)
Replace the paragraph:
case_statement ::= 
   case selecting_expression is
       case_statement_alternative
      {case_statement_alternative}
   end case;
by:
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;
!corrigendum 5.4(3)
Insert after the paragraph:
case_statement_alternative ::= 
   when discrete_choice_list =>
      sequence_of_statements
the new paragraph:
conditional_case_statement_alternative ::= 
   when choice_condition_list =>
      sequence_of_statements
!corrigendum 5.4(11/3)
Replace the paragraph:
For the execution of a case_statement the selecting_expression is first evaluated.
by:
For the execution of a case_statement with a selecting_expression, the selecting_expression is first evaluated.
!corrigendum 5.4(13/3)
Insert after the paragraph:
Otherwise (the value is not covered by any discrete_choice_list, perhaps due to being outside the base range), Constraint_Error is raised.
the new paragraph:
For the evaluation of a case_statement without a selecting_expression, all of the choice_conditions are evaluated. If exactly one choice_condition is True, the sequence_of_statements of the conditional_case_statement_alternative containing this choice_condition is executed. Otherwise (no choice_condition is True, or multiple choice_conditions are True), Program_Error is raised.
!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 <condition> {, <condition>} => <dependent_expression>{,
            when <condition> {, <condition>} => <dependent_expression>)

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 <name elided>, 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 <variable list> is
  when <choices list> is => <statements list>
  ...
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, <variable list> could be a record variable whose components are 
all discrete, or an array of static size and discrete components. The 
<choices list> 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 <lengthy-prefix>.Height, <lengthy-prefix>.Width is

Might be worth it for the other advantages.

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

From: Randy Brukardt
Sent: Wednesday, July 17, 2019  10:58 PM

In AI12-0214-2, we have the following rule for when a dependent_expression is
statically unevaluated:

Add after 4.9(32.5/3):

 * a dependent_expression of a case_expression whose associated 
   choice_condition is static and whose value equals False; or

However, a dependent_expression can have a number of associated 
choice_conditions (it is a condition_choice_list, after all), and the 
dependent_expression is only unevaluated if they are all static and false.
(If any are non-static or static and True, then the dependent_expression 
might be evaluated.)

I suggest rewording this as:

Add after 4.9(32.5/3):

 * a dependent_expression of a case_expression whose associated 
   choice_conditions are all static and all of whose values equal False; or

Since this is a reasonably obvious change, I'm just going to make it as an 
editorial change (a pretty expansive definition of editorial, I know) unless
someone objects.

An editorial change: we currently have the syntax of 

condition_choice_list ::= choice_condition {'|' choice_conditiion

In other words, we have a list of choice_conditions called a 
condition_choice_list. That doesn't match existing practice (for instance, for 
a discrete_choice_list), not to mention being stupid. So I'm going to change 
this name to choice_condition_list.

(It would be a bad idea to change the name of a choice_condition, since it is 
derived from a choice_expression in the same way that a condition is derived 
from an expression. And it's used a lot in the wording. :-)

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

From: Tucker Taft
Sent: Thursday, July 18, 2019  7:11 AM

We should consider simplifying the syntax instead, to only allow one 
condition, given the ability to use "or else".  I worry that if we start 
equating "|" and "or else" it will lead to further confusion elsewhere.

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

From: Randy Brukardt
Sent: Thursday, July 18, 2019  1:34 PM

> We should consider simplifying the syntax instead, to only allow one 
> condition, given the ability to use "or else".  I worry that if we 
> start equating "|" and "or else" it will lead to further confusion 
> elsewhere.

I think it's a little late for that (like 40 years, and especially since
2012). "|" essentially means "or else" in existing (and original) case 
statements -- that's certainly one of the implementations.

And it definitely means "or else" in a membership as extended in Ada 2012:

    A in B | C

means

    A = B or else A = C

4.5.2(27.1/4) says so as explicitly as possible without mentioning "|":

For the evaluation of a membership test using in whose membership_choice_list 
has more than one membership_choice, the 
tested_simple_expression simple_expression of the membership test is evaluated 
first and the result of the operation is equivalent to that of a sequence 
consisting of an individual membership test on each membership_choice combined 
with the short-circuit control form *or else*.

---

You've previously argued that a case alternative is like a membership, and a 
membership would definitely allow using "|" to separate two conditions:

    A in B > 5 | B < 5

So one would expect

    when B > 5 | B < 5 =>

to be legal (if you believe this similarity is important, which I personally 
don't).

In a style guide, I would suggest a subtle distinction between "|" and "or 
else" in this context. Use "|" to separate two logically separate conditions 
that happen to have the same result, and use "or else" (or "or") when you have 
to combine two parts of a single logical condition. But of course the generated
code is pretty much the same.

In any case, I think this particular ship sailed in Ada 2012 (if not long 
before). And I do think it is important that the two kinds of case statements 
aren't more different than they have to be, which includes allowing "|" to 
separate unrelated expressions that happen to need the same action.

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

From: Tucker Taft
Sent: Thursday, July 18, 2019  2:53 PM

I can see both sides of this argument.  As I said, we should *consider* 
simplifying the syntax.  I'd be curious what others think. 

I suppose a difference between the use of "or else" and "|" in this context 
is that the mutual-exclusion requirement applies to "when cond1 | cond2 => 
whereas it doesn't apply to "when cond1 or else cond2 =>".  That is, you get
Program_Error if both evaluate to true in the first case, while you silently
choose the "cond1 or else cond2" alternative in the second.  I guess we need
to double check the wording about this as well.

In general, I never imagined having multiple conditions in a single 
alternative, so it looks odd to me.  I could clearly get used to it, but the 
mutual exclusion implication seems easier to understand when there is only 
one condition per alternative.

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

From: Randy Brukardt
Sent: Thursday, July 18, 2019  4:14 PM

> I can see both sides of this argument.  As I said, we should
> *consider* simplifying the syntax.  I'd be curious what others think. 

Same here.
 
> I suppose a difference between the use of "or else" and "|" 
> in this context is that the mutual-exclusion requirement applies to 
> "when cond1 | cond2 => whereas it doesn't apply to "when cond1 or else 
> cond2 =>".  That is, you get Program_Error if both evaluate to true in 
> the first case, while you silently choose the "cond1 or else cond2"
> alternative in the second.  I guess we need to double check the 
> wording about this as well.

This is definitely the case, as the mutual exclusion requirement is worded
in terms of choice_conditions (by ARG request) and not in terms of 
conditional_case_expression_alternatives. Specifically:

... If exactly one choice_condition 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_condition is 
True, or multiple choice_conditions are True), Program_Error is raised.

So it's more "xor" than "or" in this case.

> In general, I never imagined having multiple conditions in a single 
> alternative, so it looks odd to me.  I could clearly get used to it, 
> but the mutual exclusion implication seems easier to understand when 
> there is only one condition per alternative.

It makes sense to allow multiple conditions, in that one might want to avoid 
duplication in alternatives. If an alternative is reasonably complex, why 
would anyone want to duplicate it?

  when Post =>
    (case is
     when X.A > 0 | X.A = 0 and X.B > 0 =>
        True
     when X.A = 0 and X.B = 0 =>
        Arg'Old = None
     when X.A < 0 | X.A = 0 and X.B < 0 =>
        False);

There are five separate cases here, but some of them share results. Using 
"or else" would lose information, and no one really remembers what "xor"
means. In this case, there isn't much penalty to duplicating the alternatives, 
but what if the dependent_expression is several lines long (common in 
postconditions)? So I lean toward the current definition (which allows the 
above).

In any case, I don't feel too strongly about this (even if the above appears 
that way), so let's hear from others.

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

From: Richard Wai
Sent: Thursday, July 18, 2019  10:59 PM

> > I can see both sides of this argument.  As I said, we should
> > *consider* simplifying the syntax.  I'd be curious what others think.
> 
> Same here.

It is bad enough that we are violating one of the greatest features of regular
case statements: static safety. I can't see the value in making things any 
more inconsistent. If regular case statements accept multiple conditions, so 
should case_expressions, IMO. 

[Editor's note: The rest of this message is found in AI12-0341-1.]

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

From: Jean-Pierre Rosen
Sent: Friday, July 19, 2019  12:19 AM

I'd rather have '|' on the ground of least surprise, by analogy to the normal 
case. Also, the mutual exclusion adds value to the construct.

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

Questions? Ask the ACAA Technical Agent