CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Functional companion to Kotlin's Standard Library providing type-safe error handling and functional programming constructs for iOS x64 target

Pending
Overview
Eval results
Files

partial-results.mddocs/

Partial Results

The Ior (Inclusive OR) type represents computations that can have both success values and accumulated context/warnings simultaneously. Unlike Either which is exclusive (either error OR success), Ior can represent Left (only error), Right (only success), or Both (error AND success).

Core Types

sealed class Ior<out A, out B>

data class Left<out A>(val value: A) : Ior<A, Nothing>()

data class Right<out B>(val value: B) : Ior<Nothing, B>()

data class Both<out A, out B>(val leftValue: A, val rightValue: B) : Ior<A, B>()

// Type alias for NonEmptyList context
typealias IorNel<A, B> = Ior<Nel<A>, B>

Construction

Direct Construction

// Create Left, Right, or Both
fun <A> A.leftIor(): Ior<A, Nothing>
fun <A> A.rightIor(): Ior<Nothing, A>
fun <A, B> Pair<A, B>.bothIor(): Ior<A, B>

Static Constructors

companion object Ior {
    // From nullable values - returns null if both are null
    fun <A, B> fromNullables(a: A?, b: B?): Ior<A, B>?
    
    // NonEmptyList variants
    fun <A, B> leftNel(a: A): IorNel<A, B>
    fun <A, B> bothNel(a: A, b: B): IorNel<A, B>
}

Inspection

// Type checking
fun isLeft(): Boolean
fun isRight(): Boolean
fun isBoth(): Boolean

// Predicate-based checking
fun isLeft(predicate: (A) -> Boolean): Boolean
fun isRight(predicate: (B) -> Boolean): Boolean
fun isBoth(leftPredicate: (A) -> Boolean, rightPredicate: (B) -> Boolean): Boolean

// Value extraction
fun getOrNull(): B? // Returns right value or null
fun leftOrNull(): A? // Returns left value or null

Transformation

// Transform right values (preserves left in Both case)
fun <D> map(f: (B) -> D): Ior<A, D>

// Transform left values (preserves right in Both case)  
fun <C> mapLeft(fa: (A) -> C): Ior<C, B>

// Monadic bind - requires combine function for left values
fun <D> flatMap(combine: (A, A) -> A, f: (B) -> Ior<A, D>): Ior<A, D>

// Error handling - requires combine function for right values
fun <D> handleErrorWith(combine: (B, B) -> B, f: (A) -> Ior<D, B>): Ior<D, B>

// Swap left and right types
fun swap(): Ior<B, A>

Pattern Matching

// Three-way fold
fun <C> fold(fa: (A) -> C, fb: (B) -> C, fab: (A, B) -> C): C

Conversion

// Convert to Either (ignores left value in Both case)
fun toEither(): Either<A, B>

// Convert to Pair with nullables
fun toPair(): Pair<A?, B?>

// Isomorphic conversion
fun unwrap(): Either<Either<A, B>, Pair<A, B>>

// Extract right value with default for Left
fun getOrElse(default: (A) -> B): B

Combining Ior Values

// Combine two Ior values
fun combine(
    other: Ior<A, B>, 
    combineA: (A, A) -> A, 
    combineB: (B, B) -> B
): Ior<A, B>

// Flatten nested Ior
fun Ior<A, Ior<A, B>>.flatten(combine: (A, A) -> A): Ior<A, B>

Comparison

// Compare Ior values (when components are Comparable)
operator fun <A : Comparable<A>, B : Comparable<B>> compareTo(other: Ior<A, B>): Int

NonEmptyList Context

// Convert to NonEmptyList context form
fun <A, B> Ior<A, B>.toIorNel(): IorNel<A, B>

Usage Examples

Basic Operations

import arrow.core.*

// Create different Ior variants
val leftOnly = "warning".leftIor<String, Int>()        // Left("warning")
val rightOnly = 42.rightIor<String, Int>()             // Right(42)
val both = Ior.Both("warning", 42)                     // Both("warning", 42)

// Transform values
val doubled = rightOnly.map { it * 2 }                 // Right(84)
val bothDoubled = both.map { it * 2 }                  // Both("warning", 84)

// Extract values
val result1 = rightOnly.getOrElse { 0 }                // 42
val result2 = leftOnly.getOrElse { 0 }                 // 0
val result3 = both.getOrElse { 0 }                     // 42 (has right value)

Accumulating Context

import arrow.core.*

// Parsing with warnings
fun parseNumber(input: String): Ior<List<String>, Int> {
    val warnings = mutableListOf<String>()
    
    if (input.startsWith("0") && input.length > 1) {
        warnings.add("Leading zero detected")
    }
    
    return input.toIntOrNull()?.let { number ->
        if (warnings.isEmpty()) {
            number.rightIor()
        } else {
            Ior.Both(warnings, number)
        }
    } ?: "Invalid number format".nel().leftIor()
}

val results = listOf("007", "42", "abc", "0123").map { parseNumber(it) }
// Results:
// Both(["Leading zero detected"], 7)
// Right(42)  
// Left(["Invalid number format"])
// Both(["Leading zero detected"], 123)

Computation with Warnings

import arrow.core.*

data class ValidationResult(val warnings: List<String>, val errors: List<String>)

fun validateUser(name: String, email: String): Ior<List<String>, User> {
    val warnings = mutableListOf<String>()
    val errors = mutableListOf<String>()
    
    // Name validation
    when {
        name.isBlank() -> errors.add("Name is required")
        name.length < 2 -> warnings.add("Name is very short")
    }
    
    // Email validation  
    when {
        email.isBlank() -> errors.add("Email is required")
        !email.contains('@') -> errors.add("Invalid email format")
        !email.contains('.') -> warnings.add("Email might be missing domain")
    }
    
    return when {
        errors.isNotEmpty() -> errors.leftIor()
        warnings.isNotEmpty() -> Ior.Both(warnings, User(name, email))
        else -> User(name, email).rightIor()
    }
}

val valid = validateUser("Alice", "alice@example.com")
// Right(User("Alice", "alice@example.com"))

val warnings = validateUser("Al", "alice@localhost")  
// Both(["Name is very short", "Email might be missing domain"], User("Al", "alice@localhost"))

val errors = validateUser("", "invalid")
// Left(["Name is required", "Invalid email format"])

Chaining with Context Accumulation

import arrow.core.*

fun processData(input: String): Ior<List<String>, ProcessedData> {
    return parseInput(input)
        .flatMap(::combine) { parsed -> validate(parsed) }
        .flatMap(::combine) { validated -> transform(validated) }
}

fun combine(warnings1: List<String>, warnings2: List<String>): List<String> =
    warnings1 + warnings2

// Each step can add warnings while still producing a result
fun parseInput(input: String): Ior<List<String>, ParsedData> = TODO()
fun validate(data: ParsedData): Ior<List<String>, ValidatedData> = TODO()  
fun transform(data: ValidatedData): Ior<List<String>, ProcessedData> = TODO()

Pattern Matching

import arrow.core.*

fun handleResult(result: Ior<String, Int>): String = result.fold(
    fa = { error -> "Error: $error" },
    fb = { value -> "Success: $value" },
    fab = { error, value -> "Warning: $error, but got result: $value" }
)

val warning = Ior.Both("deprecation warning", 42)
println(handleResult(warning)) 
// "Warning: deprecation warning, but got result: 42"

Install with Tessl CLI

npx tessl i tessl/maven-io-arrow-kt--arrow-core-iosx64

docs

collection-extensions.md

error-handling.md

index.md

optional-values.md

partial-results.md

product-types.md

raise-dsl.md

safe-collections.md

utility-functions.md

tile.json