dart-lang / dart-lang/language
`Never()` factory constructor
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
I often find myself wanting to mark some point in the code a unreachable. The type system often requires something, but the reason that the path is infeasible that is outside of what can be expressed in the type system. The usual remedy is to throw an exception, with there being a choice of many corelib errors - `UnimplementedError`, `UnsupportedError`, `StateError` and `AssertionError`.
Using `UnimplementedError` is nice since it does not require inventing a message.
```dart
throw UnimplementedError();
```
However, the documentation stays `UnimplementedError` is not really for this purpose: "If the class does not intend to implement the feature, it should throw an UnsupportedError instead.". And it is confusing since it leads to the question 'what is not implemented?'.
So I might use `UnsupportedError`. That requires me to invent a message.
```dart
throw UnsupportedError('Unreachable');
```
This too is not an ideal semantic match, since this kind of error is for when "The operation was not allowed by the object.". There is often no _operation_ that can easily be identified, or _object_ that is to blame; rather there is some intended invariant that must have been broken.
A `StateError` is not appropriate...
```dart
throw StateError('Unreachable');
```
...because it "Should be used when this particular object is currently in a state which doesn't support the requested operation, but other similar objects might, or the object itself can later change its state to one which supports the operation."
Perhaps the closest to my intent is to throw an `AssertionError`, again I have to come up with a message:
```dart
throw AssertionError('Unreachable');
```
It feels a bit odd to for the message to be 'Unreachable' when assertions usually describe what is expected rather than what went wrong, but `AssertionError('reachable')` feels even worse - the problem is not that something is unexpected when I am here, I'm not supposed to be here in the first place.
A final `throw`-based pattern is to not throw an `Error` at all:
```dart
throw 'Unreachable';
```
Some projects have coding standards that forbid this.
Since there are five slightly unsatisfactory and somewhat verbose ways to indicate that control flow should not reach somewhere, we should add another one that is at least concise and directly tied to the type system:
```dart
Never();
```
The `Never()` factory constructor throws an error to ensure that the expression does not have a value.
`Never()` succinctly expresses that I don't expect to get here and does not require that I invent a message that gets baked into the compiled program. When I have to write `throw SomeError('Unreachable')` I am writing 'unreachable' in three different spellings when once should be sufficient.
I don't really mind exactly what kind of error is thrown, and if the expression `Never()` is explained as a factory constructor, we should probably make it work like other factory constructors and permit a `Never.new` tear-off.
Contributor guide
Assessment
This issue has not been assessed yet.