Skip to content

For readability, perhaps mark an if statement as not completing normally? #3080

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

Open
eernstg opened this issue May 17, 2023 · 1 comment
Open
Labels
small-feature A small feature which is relatively cheap to implement.

Comments

@eernstg
Copy link
Member

eernstg commented May 17, 2023

Cf. dart-lang/sdk#59148, where it is argued that else should be avoided in the case where it has no effect (because the then-part always returns).

Arguably, less heavily indented code is easier to read than deeply indented code. For example:

void main() {
  if (condition1) {
    cleanupAsNeeded();
    return something;
  } else {
    if (condition2) {
      cleanupAsNeeded();
      return something;
    } else {
      if (condition3) {
        cleanupAsNeeded();
        return something;
      } else {
        // Finally, the main case where everything was OK.
        doTheRealWork();
      }
    }
  }

  // Versus ..

  if (condition1) {
    cleanupAsNeeded();
    return something;
  }
  if (condition2) {
    cleanupAsNeeded();
    return something;
  }
  if (condition3) {
    cleanupAsNeeded();
    return something;
  }

  // Finally, the main case where everything was OK.
  doTheRealWork();
}

The point is that the largest part of the code is performing various checks and potentially returning early (it could also have any other non-normal completion, e.g., throw something, etc.), and it is quite easy to read those if-statements as "finish right here" constructs once we get the hang of it.

Of course, an early return may be successful or it may report a failure, but the point is that we know "if we go in here then we will end the execution of this function body, we won't reach the statements after this if statement".

However, it might be useful for readability purposes if we made the status of those if-statements explicit:

void main() {
  end if (condition1) {
    cleanupAsNeeded();
    return something;
  }
  end if (condition2) {
    cleanupAsNeeded();
    return something;
  }
  end if (condition3) {
    cleanupAsNeeded();
    return something;
  }

  // Finally, the main case where everything was OK.
  doTheRealWork();
}

The choice of the word end as a keyword (no need to make it built-in, it is immediately followed by a reserved word) is of course arbitrary. It could be return (but that wouldn't match so well if it actually ends with throw something), and it could be many other things. I'm just using end here as a strawman.

It would be a compile-time error unless the then part of an end if statement is guaranteed to return, throw, etc. (that is, unless it is guaranteed that it will not complete normally). We could allow an else part, too, but it must again be guaranteed that it does not complete normally.

break and continue are somewhat tricky: Should we allow L: while (cond1) { end if (cond2) { break L; }}? I would assume that it's so useful to be able to say that "end if means that we stop running code in this function body", no exceptions, so that would probably be an error. Just delete end if you wish to do that.

The point is that we get a very prominent visual representation of the fact that this if statement must return, throw, etc., and this makes it easier to grasp what is going on when reading the code.

We could use a similar approach with loops: end while (cond) {...} would signal that if we run the loop body at least once then it won't continue with the next statement, it will return/throw/etc. Similarly for switch statements, and possibly some other constructs.

@eernstg eernstg added the small-feature A small feature which is relatively cheap to implement. label May 17, 2023
@munificent
Copy link
Member

Maybe there's a way to combine this with dart-archive/linter#2537? Let's say we had some syntax for guard-like statements that have a then branch which is executed when the pattern fails to match and where the then branch must exit.

We could possibly allow you use it less like an if-case and more like an if where instead of a pattern match, you just have a simple boolean conditional. But the restriction that the the then branch must exit would remain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
small-feature A small feature which is relatively cheap to implement.
Projects
None yet
Development

No branches or pull requests

2 participants