Look Before You Leap

A style of error handling (actually error avoidance). An example in PythonLanguage:

 a = 0
 b = 3

if a != 0: return b / a else: print "Divided by zero!" return 0
Contrast with EasierToAskForgivenessThanPermission (CoupleLeapingWithLooking):

 a = 0
 b = 3
 try:
     return b / a 
 except ZeroDivisionError?:
     print "Divided by zero!" 
     return 0

In PythonLanguage, CoupleLeapingWithLooking is the preferred idiom; in JavaLanguage and CeePlusPlus, LookBeforeYouLeap is the preferred idiom.


As another example: a Banking application. You have a Customer class with a withdraw(Money amount) method. The withdraw method throws am InsufficientFundsException if he tries to withdraw too much money. The Customer class also has a method canWithdraw(Money amount), returning true if he has sufficient funds. The InsufficientFundsException is still thrown by the withdraw method, if someone is stupid enough to call the withdraw method without checking first:

  Customer theCustomer = ...
  Money amount = new Money(500, Currency.EURO);
  if (theCustomer.canWithdraw(amount)) {
    theCustomer.withdraw(amount);
  } else {
    // tell the user he tries to withdraw too much
  }
If in a multi user system, a problem would arise because this isn't an atomic operation. If another system withdraws funds after the test and before the call to withdraw then the test was useless. You still have to deal with the case that withdrawal fails so the code would look like this, unless you synchronize the operations (which is also a perfectly legitimate way of handling this):

  if (theCustomer.canWithdraw(amount)) {
    try {
      theCustomer.withdraw(amount);
    } catch (InsufficientFundsException e) {
      notifyUserOfInsufficientFunds(amount);
    }
  } else {
    notifyUserOfInsufficientFunds(amount);
  }
Also, there may be multiple reasons why a user can't withdraw, for example, the cash dispenser may be out of money. As you'll want to let the user know why the withdrawal failed, your code may start to exhibit the ArrowAntiPattern:

  if (theCustomer.canWithdraw(amount)) {
    if (theDispenser.hasEnoughMoney(money)) {
      theCustomer.withdraw(amount)
    } else {
       // tell the user the dispenser is out of money
    }
  } else {
    // tell the user he's out of money
  }


In PerlLanguage, there's the usual more than one way to do it, but you can neatly do ErrorValues with "unless" (a reverse "if"):

 error('Appropriate message here') unless condition();  # or maybe 'die "Some message"' if it's really serious

# code here onwards executed only on success of condition()
This can happen several times in a row for different conditions; this way you can catch errors and avoid the ArrowAntiPattern.


See also: the PythonCookbook (p. 169-171), ExceptionHandlingChallenge, GatedCommunityPattern, GateKeeper


Contributors: PeterDeBruycker, EricHodges, EarleMartin, other(s)



For comparison, here's the version using exceptions (CoupleLeapingWithLooking):

  try {
    theCustomer.withdraw(amount);
  } catch (WithdrawalFailureException e) {
    notifyUserOfWithdrawalFailure(e, amount);
  }

I am not sure these are apples to apples comparisons. In the first example, testing for insufficient funds is explicitly done prior to the withdraw() method and notify user is specified as a comment. In this example, the withdraw() method is responsible for detecting Insufficient Funds. Please note that if we take the code for the first example and write its notify user comment be an exception throw, then we have written the withdraw() method used in this example. --WayneMack

You're right, they aren't. It's good to look before you leap. It's also good to CoupleLeapingWithLooking. Exceptions are avoided in the first, used in the second.


I try to AvoidExceptionsWheneverPossible, exceptions should be used when encountering an exceptional state/event, a Customer with insufficient funds is not exceptional (everyone is in the red sometime), and a Dispenser without money isn't also exceptional (especially not when Christmas comes up). What I mean is, most preconditions can be verified before doing something.

Exceptional states in a Banking application are when the ATM cannot make a connection to the central banking system to check if the customer has sufficient funds, or when the database used is corrupt, or something like that. -- PeterDeBruycker

I was taught to do this as well, but the motivation was slow exception handling in C++. I've found that's premature optimization for general cases. If code is spending too much time on exceptions I'll optimize it, but readability is improved by expanding the definition of "exceptional events" to include any method failure. Then the code looks more like the desired behavior and isn't interrupted with special cases. -- EricHodges

Is the readability improved? It appears that all that is being done is to push the validation down one level. Unless there is a valid reason to decouple the error checking code from the error handling code, all that one has done is create an extraneous exception. -- AnonymousContributor?

Yes, readability is improved. See the code examples above. The code that withdraws isn't cluttered with with precondition tests. The code is easier to maintain and more reliable because the validation becomes part of an atomic operation, written OnceAndOnlyOnce instead of sprinkled around every use of that operation (hopefully). There's always a valid reason to decouple error checking from error handling. I don't know what makes an exception "extraneous". Can you explain that? -- EricHodges

Let me try some pseudo-code examples and see if that helps explain my point.

Example 1

  TopLevelFunction?()
  {
    try {
      theCustomer.withdraw(amount);
    } catch (WithdrawalFailureException e) {
      notifyUserOfWithdrawalFailure(e, amount);
    }
  }

withdraw(amount) { if (canWithdraw(amount)) { doWithdraw(amount); } else { throw (WithdrawalFailureException e) } }

doWithdraw(amount) { //Implement money withdraw }

Example 2

  TopLevelFunction?()
  {
    if (theCustomer.canWithdraw(amount)) {
      theCustomer.withdraw(amount);
    } else {
      notifyUserOfWithdrawalFailure(amount);
    }
  }

withdraw(amount) { //Implement money withdraw }

In Example 1, we add another level to the process and end up doing error evaluation twice; once to determine the exception to throw and once to evaluate the exception thrown. Unless there is a reason to decouple notifyUserOfWithdrawalFailure() from the canWithdraw()and withdraw() functions, I would argue that the exception serves no purpose and, hence, is extraneous. If there is a need to decouple the notification function, then exceptions provide one means to accomplish this. For example, I seriously doubt anyone would truly lock in 'print "divide by zero"' for an error notification (as shown at the top of the page). Unless there is a need to decouple the error handling from the error detection, throwing exceptions on error detection and catching them for error notification is unnecessary.

The exception serves the same purpose any exception serves (notifying the calling method of failure). It lets us decouple calling code from the preconditions and internal dependencies of the withdraw() method. The 2nd example relies on the caller of withdraw() to know about and check all preconditions and to guarantee that, once checked, they don't change before or during the call to withdraw(). When preconditions change every call must change as well. -- EricHodges


It seems reasonable to argue that CoupleLeapingWithLooking is better when it comes to concurrent operations, where locking is required. It can both keep locking at the lower levels (so they won't be forgotten by top-level users, improving maintenance) and keeps locks as short as possible.

 TopLevelProcedure?(){
    theCustomer.withdraw(amount)
    catch (WithdrawalFailureException e) {
      notifyUserOfWithdrawalFailure(e, amount)
    }
 }
 withdraw(amount) {
    lock();  fini{ unlock() } -- *pardon the syntax, if you aren't familiar with it.
    if (canWithdraw(amount)) doWithdraw(amount)
    else                     throw (WithdrawalFailureException e)
 }

('fini' is my PrivateLanguage's version of 'finally'; the block will run if 'fini' is reached, but won't run until leaving the surrounding block. 'fini' blocks are run in reverse order of appearance. They're rather nice to combine with macros because cleanup becomes implicit.)


This has become a popular acronym: LBYL. Cf EAFP (DontAskPermission)


CategoryException


EditText of this page (last edited July 16, 2011) or FindPage with title or text search