Description
It has to be said...
The one thing I miss moving from Java to Dart is the lack of checked exceptions.
It makes it really hard to put complete error handling mechanisms in place if you don't know the full set of exceptions that a method can throw.
I find it rather ironic that dart doesn't have checked exceptions whilst 'Effective Dart' lints generate an error essentially saying 'check your exceptions'.
try {
Directory(path).createSync(recursive: recursive);
}
catch (e) {
throw CreateDirException(
'Unable to create the directory ${absolute(path)}. Error: ${e}');
}
generates the following lint:
Avoid catches without on clauses.
To fix this lint I need to read source code to discover the exceptions that are going to be thrown.
Exceptions were created to stop people ignoring error.
Unchecked exceptions encourages people to ignore errors.
Have we learnt nothing?
I know this probably won't get anywhere due to the current religious movement against checked exceptions and the disruption to the dart eco system but this implementation decision was simply a bad idea.
Activity
julemand101 commentedon May 23, 2020
I do agree but you should properly move this issue to the language project: https://github.com/dart-lang/language/issues since the language itself does not support checked exceptions.
lrhn commentedon May 26, 2020
Most languages created after Java has learned from Java and not introduced checked exceptions.
They sound great in theory, but in practice they are (rightfully or not) vilified by the people having to maintain the throws clauses. I think the general opinion is that it's technically a good feature, but it doesn't carry its own weight.
I don't see Dart going that way at the current time.
You can write
on Object catch (e)
if you just want to satisfy the lint. Or you can drop the lint.If you actually want to catch the exception being thrown (and you should when it's an exception, not an
Error
), then the method documentation should document it clearly.The standard way to document exceptions is a paragraph starting with "Throws ...". I admit that not all code follows that standard, and
dart:io
suffers from being written before most standards were formed.bsutton commentedon May 26, 2020
And this is exactly the problem.
I would argue that checked exceptions more than carry their weight.
With modern IDE's the overhead of managing checked exceptions is largely automated.
The lesson that doesn't seem to have been learned is that developers are lazy and inconsistent (myself included) and if we don't force them to directly address errors then they simply ignore them and the quality of software suffers as a result.
I use a fair amount of flutter plugins and largely there is simply no documentation on what errors can be generated.
Dart actually makes it harder to document errors as a developer now actually has to write documentation.
Checked errors force devs to document their errors and actually make it easier as the IDE inserts them.
The end result is that for a large chunk of the code base we use on a daily basis, errors are simply not documented and we have to do additional testing and investigation to determine what errors need to be dealt with.
In the business world they have the concept of 'opportunity cost' which is essentially if I invest in 'A', I can't invest in 'B'. What is the cost not doing 'B'? That is the opportunity cost.
With unchecked exceptions we wear the cost of maintaining the checked exceptions but we don't have to wear the cost of investigating what errors can be generated nor the debugging time spent because we didn't handle an exception in the first place.
If a library maintainer has to spend time to declare the checked exceptions that time is more than offset by developers that use that library not having to investigate/test for what errors will be generated.
I believe the backlash against checked exception is because most developers prefer to ignore errors and checked exceptions require them to manage them.
munificent commentedon May 28, 2020
I think if a function's failure values are so important to be handled that you want static checking for them, then they should part of the function's return type and not an exception. If you use sum types or some other mechanism to plumb both success and failure values through the normal return mechanism of the function, then you get all of the nice static checking you want from checked exceptions.
More pragmatically, it's not clear to me how checked exceptions interact with higher-order functions. If I pass a callback to
List.map()
that can throw an exception, how does that fact get propagated through the type ofmap()
to the surrounding caller?You describe the movement away from checked exceptions as "religious", but I think that's an easy adjective to grab when you disagree with the majority. Is there a "religious movement" towards hand washing right now, or is it actually that the majority is right and hand washing is objectively a good idea? If almost everyone doesn't like checked exceptions, that seems like good data that it's probably not a good feature.
You're probably right. But if you assume most developers are reasonable people, then that implies that it should be relatively easy to ignore errors. If it harmed them to do it, they wouldn't do it. Obviously, individuals make dumb short decisions all the time, but at scale I think you have to assume the software industry is smart enough to not adopt practices that cause themselves massive suffering.
bsutton commentedon May 29, 2020
bsutton commentedon May 29, 2020
Just a final thought.
Why would you move from a system (checked exceptions) which automates the documentation of code to one that requires developers to document their code.
The empirical evidence (again look at pub.dev) is that developers do a terrible job of documenting and view documentation as a burden.
Managing checked exceptions is a far smaller burden than documenting code.
The lack of checked exception really has to be the best example of developers shooting themselves in the foot :)
leafpetersen commentedon May 29, 2020
My general take on this is that effect type systems (of which checked exceptions is one) tend to interact badly with higher-order code, and modern languages including Dart have leaned in heavily to using first class functions in libraries etc. Indeed, the first search result I got when I just looked to see what Java does with this these days was this, which isn't inspiring. There may be better technology for making this non-clunky now, but last I looked at it, it took some pretty hairy technology to be able to express higher-order functions that were parametric in the set of exceptions that could be thrown. Inference can help with that, but as the saying goes, now you have two problems... :)
I will admit that it personally bothers me a lot that even for well-documented Dart team owned code, I often have to go poke around in the implementation to figure out whether an exception can be thrown, and in what circumstances.
rrousselGit commentedon May 29, 2020
Exceptions are not the only way to solve these.
There are multiple languages out there with no exception mechanism at all and that do just fine.
The alternative is usually a combination of union-types and tuples and destructuring. This leads to self-documenting code, where it's impossible to ignore errors, while still being able to return valid values
For example using unions, instead of:
we'd have:
bsutton commentedon May 29, 2020
I'm not certain unions result in more readable code than catch blocks.
If I'm reading the code correctly it does provide a level of documentation but it appears that it will still allow the caller to ignore the error and fail to pass it back up.
So once again we are dependant on the developer to do the correct thing and we are stuck with undocumented code.
Tuples will have the same issues.
bsutton commentedon May 29, 2020
This appears to be a broader problem with how do you handle errors in lambda etc.
Whether its a checked/unchecked exception, error, union or tuple you still need to handle the errors in the top level function.
Error returns simply allow you to (incorrectly) ignore the error.
Your code looks nice, but does it actually behaviour correctly?
rrousselGit commentedon May 29, 2020
That is not the case.
Unions forces you to check all possible cases, or it is otherwise a compilation error.
Continuing with the code I gave previously, it would be impossible to write:
You would have to either cast the value or check the
IntegerDivisionByZeroException
case.This is in a way non-nullable types, but broadened to apply to more use cases
bsutton commentedon May 29, 2020
OK, sure.
There however seems little point to introducing a new mechanism when we already have exceptions in the language.
We could possible even start by requiring checked exceptions via a lint.
We need some language experts to comment on this.
leafpetersen commentedon May 30, 2020
Touché. I'm afraid we're the best you're going to get though - Google can't afford to hire better.
46 remaining items
ryanheise commentedon Mar 14, 2023
The primary goal is for exceptions to be "checked" so that I can be told by the compiler when my code was broken by an update.
Whether that goal be achieved via explicit (e.g. annotations or documentation) or implicit (e.g. inference) means is the means to that goal.
Another approach I don't think has been talked about enough is to introduce more stringent guidelines on what constitutes a breaking change for semantic versioning purposes. Quite often, package developers only consider visible API changes (e.g. to the method signature) when they decide to bump the major version, but they don't consider exceptions. Even if we never get checked exceptions in the language or in the linter, it would still be nice to have more stringent guidelines for package developers so that they are more aware of when they should be flagging a release as a breaking change due to hidden changes in exception handling.
bsutton commentedon Mar 14, 2023
dancojocaru2000 commentedon Mar 12, 2024
It's disappointing that error handling in Dart is bad.
Reading through this issue, one thing that surprisingly came up a lot was "but higher order functions", and, having used Swift, it's puzzling that it was considered such a big problem.
Even if this is rejected, I'll add this here so that people will know that it's definitely possible to have checked exceptions and higher order functions:
A function that
rethrows
may not throw any exceptions itself, and so it will only throw if the callbacks passed in will throw.bernaferrari commentedon Jul 11, 2024
Swift now has this: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/#Specifying-the-Error-Type
gavinliu commentedon Dec 10, 2024
Many apis in sdk throw exceptions, but for dart novices, they don't realize that they need to handle exceptions.
For example:
https://github.com/dart-lang/sdk/blob/main/sdk/lib/core/iterable.dart#L699-L705
If you are not try-catch, accidents will occur.
You all say that checked exceptions are not elegant enough and not concise enough.
But how to improve the robustness of the program? Checking sdk apis one by one is a nightmare