Arrow Either extensions¶
The asyncresult-either artifact provides helpers to bridge Arrow's Either type with AsyncResult. This is useful when working with codebases that use Arrow for error handling.
Converting Flow<Either> to Flow<AsyncResult>¶
Transform a Flow<Either<L, R>> into a Flow<AsyncResult<R>>:
val userFlow: Flow<Either<UserError, User>> = userRepository.observeUser()
userFlow.asAsyncResult()
.collect { result ->
when (result) {
is Loading -> showLoading()
is Success -> showUser(result.value)
is Error -> {
val userError = result.metadataOrNull<UserError>()
showError(userError)
}
is NotStarted -> { }
}
}
The conversion works as follows:
- Either.Right becomes Success
- Either.Left becomes Error with the left value stored in metadata
- Optionally starts with Loading (default: true)
// Without initial loading state
userFlow.asAsyncResult(startWithLoading = false)
Special handling for Throwable¶
When the left type is Throwable, it's stored in Error.throwable instead of metadata:
val dataFlow: Flow<Either<IOException, Data>> = fetchData()
dataFlow.asAsyncResult()
.collect { result ->
when (result) {
is Error -> showError(result.throwable) // IOException is here
is Success -> showData(result.value)
else -> { }
}
}
Converting Either to AsyncResult¶
Transform an Either<L, R> directly into an AsyncResult<R>:
val either: Either<Failure, User> = fetchUser()
val result: AsyncResult<User> = either.toAsyncResult()
Either.RightbecomesSuccessEither.LeftbecomesErrorwith the left value stored in metadata
Binding nested Either values¶
When you have an AsyncResult containing an Either, use bind() to flatten it:
val result: AsyncResult<Either<Failure, User>> = Success(userEither)
val unwrapped: AsyncResult<User> = result.bind()
The bind() function handles the conversion:
Success(Right(value))becomesSuccess(value)Success(Left(error))becomesErrorwith the left value in metadataLoading,NotStarted, andErrorpass through unchanged
Special handling for Throwable¶
When the left type is Throwable, it's stored in Error.throwable instead of metadata:
val result: AsyncResult<Either<Throwable, User>> = Success(Left(IOException()))
val bound: AsyncResult<User> = result.bind() // Error with throwable set
Raise interop¶
If you're using Arrow's Raise DSL, you can bind an AsyncResult<T> directly in a Raise<Error> context:
import arrow.core.raise.either
val either = either<Error, User> {
repository.getUser().bind()
}
Behavior:
- Success(value) returns value
- Error raises that same error
- Loading and NotStarted raise an Error with the state stored in metadata
Converting Flow to Either¶
Transform a Flow<AsyncResult<R>> into an Either:
val flow: Flow<AsyncResult<User>> = userFlow
val either: Either<Error, User> = flow.toEither()
This waits for the first terminal emission (Success or Error) and converts it:
SuccessbecomesEither.RightErrorbecomesEither.Left
Custom error transformation¶
If you need a specific error type, provide a transform function:
val either: Either<NetworkError, User> = flow.toEither { error ->
error.metadataOrNull<NetworkError>() ?: NetworkError.Unknown
}
By default, it attempts to extract the error type from the Error.metadata.