Skip to content

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

Open
@eernstg

Description

@eernstg

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    small-featureA small feature which is relatively cheap to implement.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions