Advertisement
Guest User

Untitled

a guest
Jun 26th, 2018
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 2.45 KB | None | 0 0
  1. // 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/).
  2.  
  3. sealed class Try<out T>
  4. data class Success<out T>(val value: T) : Try<T>()
  5. data class Failure(val error: Error, val message: String? = null) : Try<Nothing>()
  6.  
  7. // Inspired from https://github.com/grpc/grpc/blob/master/doc/statuscodes.md.
  8. enum class Error {
  9.     CANCELLED,
  10.     UNKNOWN,
  11.     INVALID_ARGUMENT,
  12.     DEADLINE_EXCEEDED,
  13.     NOT_FOUND,
  14.     ALREADY_EXISTS,
  15.     PERMISSION_DENIED,
  16.     UNAUTHENTICATED,
  17.     RESOURCE_EXHAUSTED,
  18.     FAILED_PRECONDITION,
  19.     ABORTED,
  20.     OUT_OF_RANGE,
  21.     UNIMPLEMENTED,
  22.     INTERNAL,
  23.     UNAVAILABLE,
  24.     DATA_LOSS,
  25. }
  26.  
  27. // Without any special "trick", this is how we could leverage the Try class:
  28. fun getStringThatMightFail(): Try<String> {
  29.     TODO()
  30. }
  31.  
  32. fun getStringLength(): Try<Int> {
  33.     val string: Try<String> = getStringThatMightFail()
  34.     return when (string) {
  35.         is Success -> Success(string.value.length)
  36.         is Failure -> string // Returning the string Try allows to preserve the error and message of the failure
  37.     }
  38. }
  39.  
  40. // With an adapted compiler plugin, we could instead write something like:
  41. fun getStringLength(): Try<Int> {
  42.     val string: String = getStringThatMightFail()!! // Note the '!!'
  43.     return string.length // We return an Int that is automatically wrapped into a Success by the compiler
  44. }
  45.  
  46. // which is roughly translated by the compiler to:
  47. fun getStringLength(): Try<Int> {
  48.     val stringTry: Try<String> = getStringThatMightFail()
  49.     if (stringTry is Failure) {
  50.         return stringTry
  51.     }
  52.     return Success((stringTry as Success).value.length)
  53. }
  54.  
  55. // We can already obtain a similar result using some kind of builder:
  56. sealed class Try<out T> {
  57.     companion object {
  58.         inline operator fun <T> invoke(f: Builder.() -> T): Try<T> {
  59.             return try {
  60.                 Success(Builder.f())
  61.             } catch (e: FailureException) {
  62.                 e.failure
  63.             } catch (t: Throwable) {
  64.                 Failure(Error.UNKNOWN, t.message)
  65.             }
  66.         }
  67.     }
  68.  
  69.     object Builder {
  70.         fun <T> Try<T>.get(): T = when (this) {
  71.             is Success -> value
  72.             is Failure -> throw FailureException(this)
  73.         }
  74.     }
  75.  
  76.     @PublishedApi
  77.     internal data class FailureException(val failure: Failure) : Exception()
  78. }
  79.  
  80. // Then, we can do:
  81. fun getStringLength(): Try<Int> = Try {
  82.     val string: String = getStringThatMightFail().get()
  83.     string.length // or: return@Try string.length
  84. }
  85.  
  86. // 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