CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-arrow-kt--arrow-core-iosarm64

Functional companion to Kotlin's Standard Library - Core module with iosArm64 target support

Pending
Overview
Eval results
Files

effect-system.mddocs/

Effect System

Suspended computations that can raise typed errors, providing a foundation for asynchronous and concurrent programming with proper error handling.

Capabilities

Effect<Error, A>

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
): A

Usage 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())
}

EagerEffect<Error, A>

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) }

Effect Utilities

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>()
    }

Integration with Coroutines

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

docs

collection-extensions.md

core-data-types.md

effect-system.md

index.md

raise-dsl.md

tuple-types.md

tile.json