-3

There are a lot of examples over Internet that show why you should use guards.

For example:

public double getPayAmount() {
  if (isDead){
    return deadAmount();
  }
  if (isSeparated){
    return separatedAmount();
  }
  if (isRetired){
    return retiredAmount();
  }
  return normalPayAmount();
}

The author compares the above version with

public double getPayAmount() {
  double result;
  if (isDead){
    result = deadAmount();
  }
  else {
    if (isSeparated){
      result = separatedAmount();
    }
    else {
      if (isRetired){
        result = retiredAmount();
      }
      else{
        result = normalPayAmount();
      }
    }
  }
  return result;
}

... and of course, the first one looks better.

But the author (all of the authors) ignores the fact that the first version can be changed to

public double getPayAmount() {
  if (isDead){
    return deadAmount();
  }
  else if (isSeparated){
    return separatedAmount();
  }
  else if (isRetired){
    return retiredAmount();
  }
  else {
    return normalPayAmount();
  }
}

which is not that ugly (compared to the second one).

And it makes me wonder whether guards are really so useful as authors trying to convince me. Could you show the case where guards are really make the code much better?

9
  • 1
  • 1
    I for one would argue the third example is better, because it ensures that every condition returns a value, but that's because this is a good situation to use switch/match, and a bad situation to use guards. Sep 26, 2020 at 15:10
  • 1
    @VisualMelon "this is a good situation to use switch/match, and a bad situation to use guards" - Exactly! The problem is that all of the authors demonstrate the use of guards using the examples where "switch/case" is enough.
    – user90726
    Sep 26, 2020 at 15:13
  • 1
    The logic here is not the same as switch match. With the booleans, more than one can be true at a time, and the if-then code structure prioritizes them. Ideally, there would be an enum { dead, separated, retired } where only one of these conditions can be true at once. That would be ideal for switch/match.
    – Erik Eidt
    Sep 26, 2020 at 15:22
  • 4
    I would argue that this isn't even an example of guard clauses. Guard clauses are generally for "if this unwanted condition exists, exit early and don't execute this function". This example is instead more of a strategy, as in "return different values based on certain conditions". This is why true guard clauses are typically "if(x) return y", because they're not generally any more complicated than that.
    – Eric King
    Sep 26, 2020 at 15:49

2 Answers 2

2

And it makes me wonder whether guards are really so useful as authors trying to convince me. Could you show the case where guards are really make the code much better?

Your example is biased because it uses return inside the if bodies, forcing the method to end when it enters an if. This pretty much excludes the relevance of any code after the else of any if statement that evaluates to true.

If you don't use return, then if .. if .. and if .. else if .. else behave very differently.

public double getPayAmount() {
  var result = 0;

  if (isDead){
    result += 100;
  }
  else if (isSeparated){
    result += 50;
  }
  else if (isRetired){
    result + = 25;
  }

  return result;
}

Assuming all booleans are true, without the else it'd return 175, with the else it'd return 100.


But the author (all of the authors) ignores the fact that the first version can be changed to

Sure, you could use it, but that doesn't mean that it must be mentioned. When you use returning if bodies, the else is irrelevant. There's no point to having it.

Using simple step through debugging is going to explain it much better than a written answer will.

The short answer here is that for any if which returns in its body, the else can be removed without changing the flow of the code, therefore makking it irrelevant to write an else.

1
  • 1
    If the conditional block doesn't exit the function (i.e. return or raise), that's just a regular conditional branch, not a guard. The raison d'etre for a guard statement is that because you know the guard block always exits the function, you can just assume that any following blocks don't need to deal with the situation that the guard is protecting against.
    – Lie Ryan
    Sep 27, 2020 at 5:16
1

Here is an example from Implementation Patterns book by Kent Beck:

// version without guards
void compute() {
  Server server= getServer();
  if (server != null) {
    Client client= server.getClient();
    if (client != null) {
      Request current= client.getRequest();
      if (current != null)
        processRequest(current);
    }
  }
}
// version with guards
void compute() {
  Server server= getServer();
  if (server == null)
    return;
  Client client= server.getClient();
  if (client == null)
    return;
  Request current= client.getRequest();
  if (current == null)
    return;
  processRequest(current);
}

It seems this is exactly what I searched for. There are only 2 ways to write this function and the second one (i.e., with guards) is really better.

3
  • Swift: if let server = getServer(), let client = server.getClient(), let current = client.getRequest() { processRequest(current) } Or you can use a guard statement for all three required conditions. Much less code to write.
    – gnasher729
    Sep 27, 2020 at 10:03
  • @gnasher729 Did you mean this: pastebin.com/raw/53VDem1i? I suppose the longer version from Kent's book is better if we want to return different values, e.g. return "Server not found", return "Client not found", return "Current request failed".
    – user90726
    Sep 27, 2020 at 10:35
  • No, exactly as written. “If let” in Swift checks if an optional result is non-nil and unwraps it, and can contain multiple such checks separated by comma. The unwrapped optionals server, client, current are only available within the “if let” statement.
    – gnasher729
    Sep 30, 2020 at 5:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.