Functional companion to Kotlin's Standard Library - Core module with iosArm64 target support
—
Suspended computations that can raise typed errors, providing a foundation for asynchronous and concurrent programming with proper error handling.
Suspended computation that can raise errors of type Error and produce values of type A.
/**
* Suspended computation that can raise typed errors
* @param Error Type of errors that can be raised
* @param A Type of successful result
*/
typealias Effect<Error, A> = suspend Raise<Error>.() -> A
/**
* Create a suspended Effect computation
* @param block Suspended computation in Raise context
* @return Either with error or successful result
*/
suspend fun <Error, A> effect(block: suspend Raise<Error>.() -> A): Either<Error, A>
/**
* Run Effect and handle both success and error cases
* @param onError Handler for error case
* @param onSuccess Handler for success case
* @return Result of appropriate handler
*/
suspend inline fun <Error, A, B> Effect<Error, A>.fold(
onError: (Error) -> B,
onSuccess: (A) -> B
): B
/**
* Transform error type of Effect
* @param transform Function to transform error
* @return Effect with transformed error type
*/
suspend inline fun <Error, NewError, A> Effect<Error, A>.mapError(
transform: (Error) -> NewError
): Either<NewError, A>
/**
* Transform success value of Effect
* @param transform Function to transform success value
* @return Effect with transformed success type
*/
suspend inline fun <Error, A, B> Effect<Error, A>.map(
transform: (A) -> B
): Either<Error, B>
/**
* Chain Effects together
* @param transform Function that produces next Effect
* @return Chained Effect
*/
suspend inline fun <Error, A, B> Effect<Error, A>.flatMap(
transform: suspend Raise<Error>.(A) -> B
): Either<Error, B>
/**
* Recover from errors in Effect
* @param fallback Handler for error case
* @return Effect result or fallback result
*/
suspend inline fun <Error, A> Effect<Error, A>.recover(
fallback: suspend Raise<Error>.(Error) -> A
): Either<Error, A>
/**
* Handle specific error types
* @param handler Handler for specific error
* @return Effect with handled errors
*/
suspend inline fun <Error, A> Effect<Error, A>.handleError(
handler: suspend Raise<Error>.(Error) -> A
): Either<Error, A>
/**
* Get result or provide fallback
* @param fallback Fallback value provider
* @return Success value or fallback
*/
suspend inline fun <Error, A> Effect<Error, A>.getOrElse(
fallback: suspend (Error) -> A
): AUsage Examples:
import arrow.core.raise.*
import arrow.core.*
import kotlinx.coroutines.*
// Basic Effect computation
suspend fun fetchUser(id: String): Either<String, User> = effect {
val response = httpClient.get("/users/$id")
if (response.status != 200) raise("User not found: $id")
response.body<User>()
}
// Chaining Effects
suspend fun getUserProfile(id: String): Either<String, Profile> = effect {
val user = fetchUser(id).bind()
val profile = httpClient.get("/profiles/${user.id}")
if (profile.status != 200) raise("Profile not found for user: $id")
profile.body<Profile>()
}
// Error recovery
suspend fun getUserWithFallback(id: String): User =
fetchUser(id).getOrElse { User.anonymous() }
// Transforming results
suspend fun getUserName(id: String): Either<String, String> =
fetchUser(id).map { it.name }
// Handling multiple Effects
suspend fun loadDashboard(userId: String): Either<String, Dashboard> = effect {
val user = fetchUser(userId).bind()
val profile = getUserProfile(userId).bind()
val posts = async { fetchUserPosts(userId).bind() }
val friends = async { fetchUserFriends(userId).bind() }
Dashboard(user, profile, posts.await(), friends.await())
}Non-suspended computation that can raise errors, useful for synchronous operations.
/**
* Non-suspended computation that can raise typed errors
* @param Error Type of errors that can be raised
* @param A Type of successful result
*/
typealias EagerEffect<Error, A> = Raise<Error>.() -> A
/**
* Create an eager Effect computation
* @param block Computation in Raise context
* @return Either with error or successful result
*/
fun <Error, A> eagerEffect(block: Raise<Error>.() -> A): Either<Error, A>
/**
* Fold over EagerEffect result
* @param onError Handler for error case
* @param onSuccess Handler for success case
* @return Result of appropriate handler
*/
inline fun <Error, A, B> EagerEffect<Error, A>.fold(
onError: (Error) -> B,
onSuccess: (A) -> B
): B
/**
* Transform error type of EagerEffect
* @param transform Function to transform error
* @return EagerEffect with transformed error type
*/
inline fun <Error, NewError, A> EagerEffect<Error, A>.mapError(
transform: (Error) -> NewError
): Either<NewError, A>
/**
* Transform success value of EagerEffect
* @param transform Function to transform success value
* @return EagerEffect with transformed success type
*/
inline fun <Error, A, B> EagerEffect<Error, A>.map(
transform: (A) -> B
): Either<Error, B>
/**
* Chain EagerEffects together
* @param transform Function that produces next EagerEffect
* @return Chained EagerEffect
*/
inline fun <Error, A, B> EagerEffect<Error, A>.flatMap(
transform: Raise<Error>.(A) -> B
): Either<Error, B>
/**
* Recover from errors in EagerEffect
* @param fallback Handler for error case
* @return EagerEffect result or fallback result
*/
inline fun <Error, A> EagerEffect<Error, A>.recover(
fallback: Raise<Error>.(Error) -> A
): Either<Error, A>Usage Examples:
import arrow.core.raise.*
import arrow.core.*
// Basic EagerEffect computation
fun parseConfig(input: String): Either<String, Config> = eagerEffect {
val lines = input.lines()
if (lines.isEmpty()) raise("Config cannot be empty")
val pairs = lines.mapNotNull { line ->
val parts = line.split("=", limit = 2)
if (parts.size == 2) parts[0].trim() to parts[1].trim()
else null
}
if (pairs.isEmpty()) raise("No valid config entries found")
Config(pairs.toMap())
}
// Combining eager effects
fun validateAndParseConfig(input: String): Either<String, Config> = eagerEffect {
if (input.isBlank()) raise("Input cannot be blank")
val config = parseConfig(input).bind()
if (!config.hasRequiredFields()) raise("Missing required configuration fields")
config
}
// Error transformation
fun parseConfigWithDetails(input: String): Either<ConfigError, Config> =
parseConfig(input).mapError { ConfigError.ParseFailure(it) }Additional utilities for working with Effects.
/**
* Catch exceptions and convert to Either
* @param block Computation that might throw
* @return Either with caught exception or result
*/
suspend inline fun <A> catch(block: suspend () -> A): Either<Throwable, A>
/**
* Catch specific exception types
* @param block Computation that might throw
* @return Either with caught exception or result
*/
suspend inline fun <reified E : Throwable, A> catchSpecific(
block: suspend () -> A
): Either<E, A>
/**
* Convert nullable result to Option Effect
* @param block Computation that returns nullable
* @return Option with result
*/
suspend inline fun <A> ensureNotNull(block: suspend () -> A?): Option<A>
/**
* Parallel execution of Effects
* @param effects Collection of Effects to run in parallel
* @return List of results or first error encountered
*/
suspend fun <Error, A> Iterable<suspend () -> Either<Error, A>>.parTraverse(): Either<Error, List<A>>
/**
* Parallel execution with error accumulation
* @param combineError Function to combine errors
* @param effects Collection of Effects
* @return Results and/or accumulated errors
*/
suspend fun <Error, A> Iterable<suspend () -> Either<Error, A>>.parTraverseAccumulating(
combineError: (Error, Error) -> Error
): Either<Error, List<A>>
/**
* Race between Effects, returning first successful result
* @param effects Effects to race
* @return First successful result or combined errors
*/
suspend fun <Error, A> race(vararg effects: suspend () -> Either<Error, A>): Either<Error, A>
/**
* Timeout Effect computation
* @param timeoutMillis Timeout in milliseconds
* @param block Effect computation
* @return Result or timeout error
*/
suspend fun <Error, A> withTimeout(
timeoutMillis: Long,
block: suspend Raise<Error>.() -> A
): Either<Error, A>Usage Examples:
import arrow.core.raise.*
import arrow.core.*
import kotlinx.coroutines.*
// Exception handling
suspend fun safeApiCall(url: String): Either<Throwable, ApiResponse> = catch {
httpClient.get(url).body<ApiResponse>()
}
// Parallel execution
suspend fun loadUserData(userId: String): Either<String, UserData> = effect {
val effects = listOf(
{ fetchUser(userId) },
{ fetchUserPosts(userId) },
{ fetchUserFriends(userId) }
)
val results = effects.parTraverse().bind()
UserData(results[0] as User, results[1] as List<Post>, results[2] as List<User>)
}
// Racing effects
suspend fun fetchFromMultipleSources(query: String): Either<String, SearchResult> = race(
{ searchPrimaryDb(query) },
{ searchSecondaryDb(query) },
{ searchCache(query) }
)
// Timeout handling
suspend fun fetchWithTimeout(url: String): Either<String, String> =
withTimeout(5000) {
httpClient.get(url).body<String>()
}Effects integrate seamlessly with Kotlin Coroutines for concurrent and asynchronous programming.
/**
* Convert Effect to Deferred
* @param scope Coroutine scope
* @param effect Effect to convert
* @return Deferred Either result
*/
fun <Error, A> CoroutineScope.async(
effect: suspend Raise<Error>.() -> A
): Deferred<Either<Error, A>>
/**
* Launch Effect in coroutine
* @param scope Coroutine scope
* @param effect Effect to launch
* @return Job for the launched coroutine
*/
fun <Error> CoroutineScope.launch(
effect: suspend Raise<Error>.() -> Unit,
onError: (Error) -> Unit = {}
): Job
/**
* Convert Flow to Effect
* @param flow Flow to collect
* @return Effect that collects the flow
*/
suspend fun <Error, A> Flow<Either<Error, A>>.collectEffect(): Either<Error, List<A>>Usage Examples:
import arrow.core.raise.*
import arrow.core.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
// Concurrent Effects
suspend fun loadUserDashboard(userId: String): Either<String, Dashboard> =
coroutineScope {
val userDeferred = async { fetchUser(userId) }
val postsDeferred = async { fetchUserPosts(userId) }
val friendsDeferred = async { fetchUserFriends(userId) }
effect {
val user = userDeferred.await().bind()
val posts = postsDeferred.await().bind()
val friends = friendsDeferred.await().bind()
Dashboard(user, posts, friends)
}
}
// Flow integration
suspend fun processUserStream(userIds: Flow<String>): Either<String, List<User>> =
userIds
.map { fetchUser(it) }
.collectEffect()Install with Tessl CLI
npx tessl i tessl/maven-io-arrow-kt--arrow-core-iosarm64@2.1.2