dart-lang / language

Design of the Dart language
Other
2.67k stars 205 forks source link

Throwable types function signature #3723

Closed gbassisp closed 6 months ago

gbassisp commented 6 months ago

Proposal

In a very short description: a feature similar to the Java throws keyword: https://docs.oracle.com/javase/tutorial/essential/exceptions/declaring.html

Motivation

This is actually a counter-proposal to this request here: https://github.com/dart-lang/language/issues/3501

I see this idea of returning a Success/Failure Result type coming up often and understand the appeal, but I disagree on this being part of Dart, as pointed out by some people in the comments. I believe this "return error" approach has its value, but belongs in a package to be used at the developer's discretion, and one of the answers here summarizes quite well: "Fortunately Dart already has a solution for that: throwing Exceptions."

However, I believe the community will continue to request it because throwing exceptions breaks control flow and, most importantly, many times we don't know what to expect. What kind of errors can you expect from a package you didn't implement? This is where I believe an implementation of throws in Dart would outshine Java original approach; Dart's excellent static analysis and type inference would be capable of knowing ahead what could go wrong and where.

Syntax

Basically, it would add 2 new keywords: throws and nothing. The latter would be used in the same sense as void, to be explicit about not throwing anything. These keywords would be an optional part of any callable (functions and operators):

A Function(B p0, C p1, ... D pn) throws<E, F, ..., G>

A simple example of this syntax would be:

void simpleFunction(bool isHappy) throws SadException {
  if (!isHappy) {
    throw SadException();
  }
}

void anotherFunction() throws SadException {
  simpleFunction(Random().nextBool());
}

Of course, this would lead to verbose function signatures, because every statement can add a new type of exception to be thrown. But this is where type inference would make this feature reasonable, by not requiring to explicitly write it.

The same example with throwable type inference would look like current Dart:

void simpleFunction(bool isHappy) { // throws SadException implied
  if (!isHappy) {
    throw SadException();
  }
}

void anotherFunction() { // throws SadException implied
  simpleFunction(Random().nextBool());
}

So why does it matter?

So, if it's optional, what would actually change?

  1. Static analysis can let you know about errors you didn't know:
    // expect lint here; I probably didn't see a possible exception here
    void anotherFunction() throws nothing {
    simpleFunction(Random().nextBool());
    }
  2. Exhaustive try/catch statements with specific behaviours for each type of exception. Tbh, I don't see myself using this point, but this could be based on Java current and it would allow more targeted catch, instead of an "pokemon trainer catch'em all" and swallow errors
  3. Static analysis can let you know about breaking changes introduced by dependencies. Considering that throwables are not part of a package API, changes to throwable types are not "breaking", because the API never changed. Which leads to...
  4. Better API documentation. The generated API docs can give us all the info we need about parameter and return types, but we have to write down in the comments possible exceptions that can be thrown and hope people will read the docs. Even Python, which has loose types and basic type annotation, has a raises keyword that can be used in docstrings and picked up in generated API docs:
    Example from a python library: https://dateparser.readthedocs.io/en/latest/dateparser.html#dateparser.date.DateDataParser.get_date_data Screenshot 2024-04-24 at 11 52 22 PM

I don't think this is something we need urgently, but I believe we should think of options that are suitable to Dart and help us plan ahead for the future.

Happy to discuss it further 😄

rubenferreira97 commented 6 months ago

This was already discussed and was rejected. Read https://github.com/dart-lang/language/issues/984.

gbassisp commented 6 months ago

This was already discussed and was rejected.

Thanks for pointing me to that! I looked for a previous request and the only one I found was the one I mentioned. 😞

But I'm glad there was a serious discussion about it and at least there is some more discussion still going on about a possible linter https://github.com/dart-lang/linter/issues/2246

I'm closing this one then