dart-lang / dart-lang/language
Why does pattern matching have such strict restrictions on non-constant values?
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
Both guard clauses and constant clauses in patterns require the use of contants where, semantically, it seems like any expression value could be used with a clear meaning. Similarly, a guard clause can only be used at the end of a `case` expression when it seems like the semantics would be fairly clear if it were allowed after any pattern as a way to "escape" into some more complex boolean logic. I'm guessing there's a good reason for this design, but it's not really explained in the docs so it's hard to integrate it with my mental model. Consider this a hybrid feature request / request for more documentation.
As a motivating example, I'm trying to convert [this conditional code](https://github.com/sass/dart-sass/blob/8dddcb7b7db13984fea69fa85438acf30b56b4bb/lib/src/ast/sass/expression/binary_operation.dart#L67-L72) to a pattern-matching style:
```dart
var left = this.left;
var leftNeedsParens = (left is BinaryOperationExpression &&
left.operator.precedence < operator.precedence) ||
(left is ListExpression &&
!left.hasBrackets &&
left.contents.length > 1);
```
The tricky bit is `left.operator.precedence < operator.precedence`. I'd like to be able to write something like
```dart
case BinaryOperationExpression(
operator: BinaryOperator(precedence: < operator.precedence)
) ||
ListExpression(hasBrackets: false, contents: [_, _, ...]) =>
true
```
but instead I think my best option is something like
```dart
BinaryOperationExpression(operator: BinaryOperator(precedence: var p))
when p < operator.precedence =>
true,
ListExpression(hasBrackets: false, contents: [_, _, ...]) =>
true
```
This works well enough in my case where the pattern isn't too deeply nested and both the right-hand sides are `true`, but it would get very nasty otherwise. And it feels like I'm essentially doing a manual transformation that the compiler could do for me. Why _not_ just allow me to write `precedence: < operator.precedence` and interpret that as a `when` clause at the end? Or even allow the pattern to eagerly fail to match if it's not evaluated?
Contributor guide
Research direction
Start with the linked dart-sass binary_operation.dart example and compare its conditional logic with the two proposed patterns. Read the existing Dart pattern-matching documentation and specification for constant patterns and when guards; done means documenting the rationale and restrictions, or recording a decided language change.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100