dart-lang / dart-lang/language

Why does pattern matching have such strict restrictions on non-constant values?

Open
#3,064 6 comments 5 reactions 0 assignees View on GitHub
request
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.