Description
(This is basically reopening #2181 as a new feature.)
Allow <expression> 'case' <pattern> ('when' <expression>)?
as an expression which:
- Evaluates to a
bool
result. - Introduces variables that are available on the
true
path out of thatbool
, if it occurs in a condition position.
Since patterns look like expressions, and can end in ?
, and the start and end of case...when
are expressions, we must know where a case expression ends.
That means it must likely be a production of <expression>
, which means it cannot be combined with any operators without being parenthesized, and it will have to be delimited in some way in all uses.
Proposed:
<expression> ::= ...
| <caseExpression>
<caseExpression> ::=
<expressionWithoutCascade> 'case' <pattern> ('when' <expressionWithoutCascade>)?
(Not absolutely sure about the placement in the grammar, but likely close to this. At least you definitely don't want an unparenthesized case-expression as the value or when clause of another case-expression.)
If the pattern introduces variables, then those variables are in scope in the when
clause as usual.
Further, if the case-expression (or parenthesized expression, or !
-negated expression) is use in a condition position, which means any position where its value directly affects control flow (has a "true" branch and a "false" branch), then the variables are available on the true path, until the end of the current constructor/block.
(This is not intended to be something new, it's exactly the same continuation where a promoting test would promote, except that the variables are also limited to the scope they are declared in.)
Example:
var x = input case Box(:var value) ? value : input;
and
{
input case Box(:var value) || throw "nope";
print(value);
}
(I know this is bad style. It does show that a variable is available in the rest of the block, as long as it's dominated by the true
test.)
If a case
expression is used as the condition of an if
statement/element, a for
statement/element or a while
statement, then the variables are available in the body (if that's the true-branch), but not outside of the satement. It is as if the if
/loop structure introduces a scope block containing the condition itself, and then the body is another nested scope.
This is consistent with how if
/case
works today, but this allows doing multiple independent cases:
if ((ex case Foo(:var x) when x > 0) && (ex2 case Bar(:var y) when y > 0)) {
both `x` and `y` in scope.
}