Exception wrapping VS throwing


#1

Which way do we usually design a method that in some circumstances is expected to fail?

  • Throw an exception from the method body. And warn caller to catch it on a calling side.
fun devide(num: Int, denom: Int): Int {
	return if (denom != 0) num / denom
	else throw ArithmeticException("division by zero") // let's pretend we should care about the division by zero manually, just for demo purposes
}

val value: Int = try {
	devide(1, 0)
} catch (x: Exception) {
	println("Oops: $x")
	-1
}
  • Or return null or Optional if we don’t want to bother a caller with an exception handling. However, the caller misses info about what’s gone wrong.
fun devide(num: Int, denom: Int): Int? {
	return if (denom != 0) num / denom
	else null
}

val value: Int = devide(1, 0) ?: run {
	println("Oops: something's gone wrong")
	-1
}

For this purpose, the Scala’s Try monad was created. A returned result can be either a Success that wraps the requested value or a Failure that wraps an exception. This approach facilitates an alternative, more convenient and intelligible code style. But as for now, there’s nothing similar in Kotlin.

Yet recently I’ve come across the nice and simple solution, proposed by Roman @elizarov here with the feature request here.

fun devide(num: Int, denom: Int): Result<Int> {
	return if (denom != 0) Result.Success(num / denom)
	else Result.Failure(ArithmeticException("division by zero"))
	// or even simpler: return resultOf{ num / denom }
}

val value: Int = devide(1, 0).onFailure{println("Oops: $it")}.getOrElse{ -1 }

To my mind, it should be as nice, simple and easy as it is.
There are alternative proposals to make it monadic i.e. the way the Scala Try or Either is. However, one essential advantage of the Result implementation above, over monadic one is that the former is optimized for a successful result. Whereas the monadic implementation is symmetric and a bit heavy. And it’s important because when I count precious nanoseconds I’d prefer “throwing and handling” style over “wrapping result” style if it takes longer than a cost of a simple method call. And for now, the Result implementation is equal or even faster than Java’s Optional.

What are your considerations?


#2

Here is a comparison of the performance of two Result implementations: monadic and optimized one.
According to measurements I came to the conclusion that:

  • the monadic implementation is about 20% slower than the optimized one. (But it’s 200 ns on my PC). And for some reason, it involves more intensive gc.
  • the monadic implementation has more convenient syntax when we deal with the smart cast: if (result is Success) result.value.
  • the exposed optimized constructor is about 30% faster than a construction via the companion functions: Result("value") / Result(Exception()) VS Result.success("value") / Result.failure(Exception())

So I give my vote for the optimized implementation of the Result with exposed constructors and value, exception properties.