Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // We could use the Try class to make error handling in Kotlin more explicit. This is similar to std::expected in C++ (https://bell0bytes.eu/expected/).
- sealed class Try<out T>
- data class Success<out T>(val value: T) : Try<T>()
- data class Failure(val error: Error, val message: String? = null) : Try<Nothing>()
- // Inspired from https://github.com/grpc/grpc/blob/master/doc/statuscodes.md.
- enum class Error {
- CANCELLED,
- UNKNOWN,
- INVALID_ARGUMENT,
- DEADLINE_EXCEEDED,
- NOT_FOUND,
- ALREADY_EXISTS,
- PERMISSION_DENIED,
- UNAUTHENTICATED,
- RESOURCE_EXHAUSTED,
- FAILED_PRECONDITION,
- ABORTED,
- OUT_OF_RANGE,
- UNIMPLEMENTED,
- INTERNAL,
- UNAVAILABLE,
- DATA_LOSS,
- }
- // Without any special "trick", this is how we could leverage the Try class:
- fun getStringThatMightFail(): Try<String> {
- TODO()
- }
- fun getStringLength(): Try<Int> {
- val string: Try<String> = getStringThatMightFail()
- return when (string) {
- is Success -> Success(string.value.length)
- is Failure -> string // Returning the string Try allows to preserve the error and message of the failure
- }
- }
- // With an adapted compiler plugin, we could instead write something like:
- fun getStringLength(): Try<Int> {
- val string: String = getStringThatMightFail()!! // Note the '!!'
- return string.length // We return an Int that is automatically wrapped into a Success by the compiler
- }
- // which is roughly translated by the compiler to:
- fun getStringLength(): Try<Int> {
- val stringTry: Try<String> = getStringThatMightFail()
- if (stringTry is Failure) {
- return stringTry
- }
- return Success((stringTry as Success).value.length)
- }
- // We can already obtain a similar result using some kind of builder:
- sealed class Try<out T> {
- companion object {
- inline operator fun <T> invoke(f: Builder.() -> T): Try<T> {
- return try {
- Success(Builder.f())
- } catch (e: FailureException) {
- e.failure
- } catch (t: Throwable) {
- Failure(Error.UNKNOWN, t.message)
- }
- }
- }
- object Builder {
- fun <T> Try<T>.get(): T = when (this) {
- is Success -> value
- is Failure -> throw FailureException(this)
- }
- }
- @PublishedApi
- internal data class FailureException(val failure: Failure) : Exception()
- }
- // Then, we can do:
- fun getStringLength(): Try<Int> = Try {
- val string: String = getStringThatMightFail().get()
- string.length // or: return@Try string.length
- }
- // I like the compiler approach more, but it's not trivial to implement. What do you think ?
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement