Skip to content

Case expressions #4343

Open
Open
@lrhn

Description

@lrhn

(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 that bool, 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.
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureProposed language feature that solves one or more problemspatternsIssues related to pattern matching.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions