-
Notifications
You must be signed in to change notification settings - Fork 213
Never()
factory constructor
#3280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I throw Or add |
In the few cases I've had to do this, I've found If you want to avoid having to write out messages in each case though, you could also just subclass class UnreachableError extends AssertionError
{
UnreachableError([super.message = "This should never execute."]) : super();
}
Never get unreachable => throw AssertionError("This should never execute."); Either of those seems like they'd communicate the intention more clearly than invoking |
I'd go with If we introduce Or make yourself a helper function: Never get unreachable {
assert(false, "Unreachable"); // With message in debug mode.
throw AssertionError(""); // Without message in production.
} (I don't know if this is technically a langauge issue, it's just asking for a single method/constructor to be added to an existing type type, a method which doesn't have to be mentioned in the language specification.) |
@lrhn I thought it should be a language issue since There was a recent issue about identifiers that denote types being treated differently depending on the type. It appeared that there was a desire that the type literals should be treated more uniformly.
I can (and do) do that, but I see
This kind of helper function has no obvious right library to be declared in, except |
I would love to see a new Error in dart:core (such as I think that constructing a Never() is not clear about the intent and could be confusing. However, something like: else {
throw UnreachableError(optionalMessage);
} would be unambiguous, clear, and could contain a descriptive message as a doc comment on the declaration of UnreachableError.
AssertionErrors could be confusing to people because asserts are not enabled in release builds of, e.g., flutter apps. I also think that implicit Never-related control flow should be discouraged. A huge +1 for UnreachableError from me! |
@modulovalue Why would it have an |
It is not unreachable, that is, we haven't provided a proof that the type system is able to verify. We only assume it to be unreachable. It is easy to introduce a change that invalidates the assumptions that made one believe that that branch is unreachable. Therefore, I have found it to be valuable to explain why I believe such a branch to be unreachable. throw UnreachableError(
"""Under the assumption A, B and C, this is unreachable. Consider
checking whether the assumptions hold if this branch is ever reached.""",
); |
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
andAssertionError
.Using
UnimplementedError
is nice since it does not require inventing a message.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.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......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: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 anError
at all: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:
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 writethrow 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 aNever.new
tear-off.The text was updated successfully, but these errors were encountered: