Handling expected failures?


#1

Hi - I am working hard as a Kotlin advocate at Cinnober, where we have a long history of Java development. Overall I find it easy to show how Kotlin is simply an improvement over Java, but there is one area where I am struggling - a replacement for checked exceptions.

I completely agree that checked exceptions lead to a whole range of problems that we do not want in Kotlin, so they are gone for a good reason. I feel though that there are cases where we want to force the caller to handle an expected failure (for instance when fetching remote resources or accessing databases) that Kotlin currently does not cover well. Scala has the solution I like best for this - Try(Failure/Success), but the limited pattern matching in Kotlin limits the usefulness of that pattern.

What is the current best practice? Are there any future plans for language features that could resolve this (such as an Either construct)?

Thank you!


#2

I feel like using null values when errors happen works nicely for simple cases. For example you could so something like

val result : Result? = query.result
if(result == null) {
    handleError(query.error)
    return
}

doSomethingWith(result)

or if you want an exception something like

val result = query.result ?: throw Exception(query.error)

#3

I’ve used Result class (similar to Rust). Probably it’s like Try. Why do you think that Kotlin when is not sufficient for this use-case? I’ve found that it was enough for my code.

Overall I prefer exceptions for error handling. I’ve used Result as a sequence type, because throwing exceptions in a sequence operation will break it, and I needed to keep pipeline functioning. I think, that unless you’re in a similar situation, you should use exceptions. But you should be able to use Try-like class, if you want.

https://gist.github.com/anonymous/670d86ad5f29c9ede09fc6aca00f5466 here’s my class and some sequence helpers.


#4

I agree that null works extremely well for the simple cases. My question is mostly about the not so simple cases where the reason for the failure, error codes and other error information may be useful.


#5

vbezhenar: Your Result implementation was interesting - it got me on the right track. Thank you! I managed to throw something together that lets me do:

val checked = fetchWebResourceAsBytes("http://someplace/someData")
when (checked) {
    is Success<ByteArray> -> doSomething(checked.result)
    is Failure -> doSomethingOnFailure(checked.error)
}

I had assumed that type erasure would be too much of a problem, but this worked out just fine. Thank you!


#6

You can also use Optional class from Java 8 which is accessible from Java. It could either contain some value or be empty. It also supports null-safe result transformation and ifPresent{} block. In Java 9 they promise full support for two branches (present and not present) via lambdas.


#7

You can handle an expected failure as result, so return a specific sealed class (Result, Error1, Error2, …).

Otherwise look at funktionale https://github.com/MarioAriasC/funKTionale


#8

Thank you - a lot of nice patterns in funKTionale!