Do not throw exceptions

Do not throw exceptions if you can possibly avoid it.

In particular, when:

Reasons

They’re referentially opaque

Throwing an Exception breaks referential transparency.

This can be demonstrated fairly easily. If throw was referentially transparent, by definition, the two following methods would be equivalent:

def foo1() = if(false) throw new Exception else 2

def foo2() = {
  val a = throw new Exception
  if (false) a  else 2
}

Turns out, however, that they aren’t.

foo1 terminates:

foo1()
// res0: Int = 2

foo2 fails with an exception:

foo2()
// java.lang.Exception
// 	at repl.Session$App$.foo2(avoid_throwing_exceptions.md:12)
// 	at repl.Session$App$$anonfun$2.apply$mcI$sp(avoid_throwing_exceptions.md:27)
// 	at repl.Session$App$$anonfun$2.apply(avoid_throwing_exceptions.md:27)
// 	at repl.Session$App$$anonfun$2.apply(avoid_throwing_exceptions.md:27)

They’re unsafe

Scala uses unchecked exceptions, which means that the compiler is not aware of them, and cannot check whether they’re dealt with properly. A function that throws is a bit of a lie: its type implies it’s total function when it’s not.

Let’s take a trivial example:

def foo(i: Int) = throw new Exception

As far as the type checker is concerned, this function is perfectly fine and it’ll happily accept the following:

foo(1)
// java.lang.Exception
// 	at repl.Session$App$.foo(avoid_throwing_exceptions.md:34)
// 	at repl.Session$App$$anonfun$3.apply(avoid_throwing_exceptions.md:41)
// 	at repl.Session$App$$anonfun$3.apply(avoid_throwing_exceptions.md:41)

This blows up at runtime, which is really something we’d like to avoid.

Exceptions to the rule

It’s perfectly fine to throw an exception for truly exceptional errors. CPU not found? Critical hard-drive failure? a required resource hasn’t been bundled with the binaries? throw away.

But scenarios such as parse this string into an int should never throw - a String not being an Int is not exceptional, it’s the normal case! There are many more Strings that aren’t valid Ints than the converse.

Checked by

LinterRule
WartRemover