Exception and explicit error managementsteemCreated with Sketch.

in #programming3 years ago

Ocaml language have a well supported exception mechanism, and also predefined types in library for explicit error management.

People have their preferences, as I have mine. a surprising aspect is that people usually tend to choose between both approach for error management. Why not use both of them with a strong focus on the one you are more at ease ?

I like explicit error management with return types including error data. If I take the example of the range library I have written as a useful toy project, interface expose functions triggering exception or returning explicit type error.

And I appreciate a lot the fact of having both functions for a feature. In real coding I need both. The fact of using both approach influence the way you use exceptions.

Exception for handling exceptional use cases

There is some situations which doesn't need a heavy error management. For example a function can trigger an error if a parameter is negative. If you pass a static value manually, you doesn't want to handle the case where the function trigger an error. What will happen is you make a mistake :

  1. the code will crash with an exception who start precisely where the problem is located, a light analyzis will be enough to understand where is the problem.
  2. This is a typical internal error, you can't give a useful message to the end user.

But what is very appreciated and not mentioned here, is the backtrace provided by the exception mechanism.

Exception call back, and stack trace

There is two tools I never needed on my code :

  1. Debugger
  2. call stack of a triggered exception

These two powerful tools are needed, for extreme situations, for fixing quickly legacy code hacked by people who aren't availables anymore. In this kind of situations, you don't understand what is happening, nor the code you are reading, you add exceptions and unit tests. Stack trace is interesting.

Now if you are in the context of a development and you are implementing the error management of the software, you doesn't need debugger or stack trace of exceptions, because you are managing the errors.

Explain to the user what is the need

A lot of errors can be triggered by the input. When the input is related to a side effect (third parties, users, database ...), you need an explicit error management. They are normal use cases of the business, exceptions aren't the best approach. Because at the end, you will need to catch it, and send a message to the user. Try catch is heavy, so personally I prefer to avoid exceptions if I need to catch them.

When you handle a problem with a user input, explain in a message what you are expected from him. If you have a problem with a third party solution, explain full with the appropriated channel what is happening and what the software need to fully operate.

Describing a problem isn't as useful of describing what need to be done to recover from the problem.

Triggering an error where the real problem is located

In an ideal world, figuring why an error has occurred is easy. You go right to the place where the error was detected and everything is available to decide how to fix it cleanly. In the real world you need to investigate, because it isn't easy to know why an error occurred.

if we take the example of a division by zero. Whatever your preferences for handling errors, exception or error types, this is too late. I will repeat, a division by zero is an error detected too late ! If this kind of error occur, there is some business logic badly implemented with inconsistencies.

A fictional challenge could be to distribute task to multiple processors, and you want to divide the number of tasks by the number of available processors. This number can be changed through an environment variable.

Here an example of a real code :

let get_cores_to_use () =
  Sys.getenv "CORES_AVAILABLE"
  >>= is_inside_int_length >>= int_from_string >>= valid_core_value
  |> value ~default:cores_available_default_value

Setting.get_cores return an integer. checking the value stored in CORES_AVAILABLE environment variable is consistent.
In this context, consistency is defined as follow :

  • string length is consistent with an integer representation
  • once converted in integer, the value is consistent with a natural number designating the number of cores

Without this checkings, if we make a division by the number of cores coming from outside world, we can have a division by zero. But if we make error handling at the right place, we doesn't trigger an error for a division by zero but at the business level, with a meaningful error, or as in the code of the example with a default value.

If you lack intuition of what level of checking is really useful, bench the resilience of your application the tool American Fuzz Loop. This will show you the kind of crappy input you can face. OCaml can be instrumented thanks to a specific instrumentation code inserted by the compiler.

So for being able to give meaningful error message, you need to detect business errors. My opinion is that business errors should be handled explicitly with for example a Return.t type, and exceptions only for exceptional situations. But if you prefer exceptions, it's okay if you raise them at the right place, business code. If you do it, you will realize that you will less often need stack trace to understand what is happening.

Coin Marketplace

STEEM 0.30
TRX 0.12
JST 0.034
BTC 63750.99
ETH 3130.22
USDT 1.00
SBD 3.95