Домой United States USA — software Kotlin: Functional Exception Handling With Try

Kotlin: Functional Exception Handling With Try

198
0
ПОДЕЛИТЬСЯ

Much like Scala, you can use the Try type in Kotlin to functionally handle exceptions. This guide walks through a basic setup and introduces chaining with maps.
Scala has a Try type to functionally handle exceptions. I wrapped my head around that concept by using the excellent Neophyte’s guide to Scala by Daniel Westheide. This post will replicate that type using Kotlin.
Consider a simple function that takes two Strings, converts them to integers, and then divides them (sample based on Scaladoc of Try):
It is the caller’s responsibility to ensure that any exception that is propagated from this implementation is handled appropriately using the exception handling mechanism of Java/Kotlin:
My objective with the «Try» code will be to transform the «divide» into something which looks like this:
A caller of this variant of the «divide» function will not have an exception to handle through a try/catch block. Instead, it will get back the exception as a value, which it can introspect and act on as needed.
The «Try» type has two implementations corresponding to the «Success» path or a «Failure» path and is implemented as a sealed class the following way:
The «Success» type wraps around the successful result of an execution while the «Failure» type wraps any exception thrown from the execution.
So now, to add some meat to these, my first test is to return one of these types based on a clean and exceptional implementation, along these lines:
This can be achieved through a «companion object» in Kotlin, similar to static methods in Java. It returns either a Success type or a Failure type based on the execution of the lambda expression:
Now that a caller has a «Try» type, they can check whether it is a «Success» type or a «Failure» type using the «when» expression like before, or using «isSuccess» and «isFailure» methods, which are delegated to the sub-types like this:
In case of Failure, a default can be returned to the caller, something like this in a test:
Again implemented by delegating to the subtypes:
The biggest advantage of returning a «Try» type, however, is in chaining further operations on the type.
A «map» operation is passed a lambda expression to transform the value in some form — possibly even to a different type:
Here, a number is being doubled and then converted to a string. If the initial Try were a «Failure», then the final value will simply return the «Failure» along the lines of this test:
Implementing «map» is fairly straightforward:
flatMap, on the other hand, takes in a lambda expression, which returns another «Try» type and flattens the result back into a «Try» type along the lines of this test:
Implementing this is simple too, along the following lines:
The «map» and «flatMap» methods are the power tools of this type, allowing chaining of complex operations together and focusing on the happy path.
Try is a powerful type, allowing a functional handling of exceptions in code. I have a strawman implementation using Kotlin available in my GitHub repo here.

Continue reading...