Skip to content
This repository has been archived by the owner on Jul 19, 2024. It is now read-only.
/ Esito Public archive

Esito ambition is to be your return type for suspending functions.

Notifications You must be signed in to change notification settings

Subito-it/Esito

Repository files navigation

Esito - Deprecated

⛔️ DEPRECATED

We have chosen to stop support and improvements on Esito since we internally migrated to Arrow. When Esito was created (at the beginning of 2021), Arrow was still in progress and was not stable at all (version 1.0.0 was released in September 2021). Basically, using Arrow, you can achieve the same results and also explore functional programming more, if you like!

Coroutines are great for Asynchronous and non-blocking programming, but exceptions could be hard to handle, especially in a coroutine. [1, 2, 3]

Exceptions in a coroutine could cancel their parents, and each canceled parent cancels all its children, and this is not always the desired behavior.

While this could be changed using a SupervisorJob, it's still easy to shoot yourself in the foots throwing exceptions in Coroutines, so Esito is proposing an alternative approach making explicit the computations that could fail.

Installation

repositories {
	maven { url 'https://jitpack.io' }
}

dependencies {
    implementation("com.github.Subito-it.Esito:core:(insert latest version)")
}

Getting started

The main class is the sealed class Result, with two subtypes:

  • Success: it contains the result of a successful computation
  • Failure: it contains the cause of an unsuccessful computation

The goal when using Esito is to avoid throwing exceptions and use Result as the return type of a function that can fail:

sealed class ConversionError
object EmptyInput : ConversionError()
object NotNumericInput : ConversionError()

fun fromStringToInt(input: String): Result<Int, ConversionError> = when {
    input.isBlank() -> Result.failure(EmptyInput)
    else -> runCatching { input.toInt() }.mapError { NotNumericInput }
}

In this example we are defining all the possible failures in ConversionError, then we are applying some logic to build our result.

If the input is not blank we are using the runCatching method to wrap a method throwing an exception and mapping the eventual error in our desired type.

Operators

Esito result has several operators, such as map and flatmap, for examples see Recipes.

Retrofit Integration

Esito ships an integration with retrofit, after a one-line setup you can start to use Result return type in your Retrofit interfaces:

interface GitHubService {
    
    @GET("users/{user}/repos")
    suspend fun listRepos(@Path("user") user: String?): Result<List<Repo>, Throwable>
}

For additional info and to learn how to use your own Error instead of Throwable have a look at this documentation.

Async Utilities

Esito offers some utilities for suspending methods returning Result. For example, suppose we have the following functions:

suspend fun getUserFullName(userId: Int): Result<String, FeatureError>

suspend fun getUserStatus(userId: Int): Result<UserStatus, FeatureError>

And we have to return an instance of DataForUI running in parallel getUserFullName and getUserStatus.

data class DataForUI(
	val userFullName: String,
	val userStatus: UserStatus
)

Esito exposes a zip method to compute two independent execution in parallel:

suspend fun fetchIfo(userId: Int): Result<DataForUI, FeatureError> =
	zip(
		{ getUserFullName(userId) },
		{ getUserStatus(userId) },
		::DataForUI //syntactic sugar for constructor
		).invoke()

For additional info have a look at this documentation.

Testing

Esito is providing two extension methods to facilitate testing: assertIsSuccess and assertIsFailure, here is an example of usage:

val success = Result.success<Int, Throwable>(42)
success.assertIsSuccess {
    assertEquals(42, value)
}

val failure = Result.failure<Int, RuntimeException>(RuntimeException("Ops"))
failure.assertIsFailure {
    assertEquals("Ops", error.message)
}

For additional info have a look at this documentation.