Functional companion to Kotlin's Standard Library providing core data types and error handling
—
Type-safe error handling that makes failure cases explicit and composable. Either is right-biased, treating success (Right) as the default path while providing extensive combinators for error handling and composition.
Create Either instances representing success or failure states.
/**
* Create a successful Either containing the given value
*/
fun <B> Either.Companion.Right(value: B): Either<Nothing, B>
/**
* Create a failed Either containing the given error
*/
fun <A> Either.Companion.Left(value: A): Either<A, Nothing>
/**
* Execute a function and catch any thrown exceptions
*/
fun <R> Either.Companion.catch(f: () -> R): Either<Throwable, R>
/**
* Execute a function and catch specific exception types
*/
inline fun <reified T : Throwable, R> Either.Companion.catchOrThrow(
f: () -> R
): Either<T, R>Usage Examples:
// Direct construction
val success: Either<String, Int> = Either.Right(42)
val failure: Either<String, Int> = Either.Left("Error occurred")
// Exception handling
val result = Either.catch { "123".toInt() } // Either<Throwable, Int>
val parsed = Either.catchOrThrow<NumberFormatException, Int> {
"abc".toInt()
} // Either<NumberFormatException, Int>Convenience functions for creating Either instances from values.
/**
* Wrap any value in Either.Right
*/
fun <A> A.right(): Either<Nothing, A>
/**
* Wrap any value in Either.Left
*/
fun <A> A.left(): Either<A, Nothing>
/**
* Create an EitherNel with a single error in NonEmptyList
*/
fun <E> E.leftNel(): EitherNel<E, Nothing>Check the state of Either instances with type-safe guards.
/**
* Check if Either is Left (error case)
* @return true if Left, false if Right
*/
fun <A, B> Either<A, B>.isLeft(): Boolean
/**
* Check if Either is Right (success case)
* @return true if Right, false if Left
*/
fun <A, B> Either<A, B>.isRight(): Boolean
/**
* Check if Either is Left and matches predicate
*/
fun <A, B> Either<A, B>.isLeft(predicate: (A) -> Boolean): Boolean
/**
* Check if Either is Right and matches predicate
*/
fun <A, B> Either<A, B>.isRight(predicate: (B) -> Boolean): BooleanSafely extract values or perform operations based on Either state.
/**
* Pattern match on Either, providing handlers for both cases
*/
fun <A, B, C> Either<A, B>.fold(
ifLeft: (A) -> C,
ifRight: (B) -> C
): C
/**
* Extract the Right value or compute a default from Left
*/
fun <A, B> Either<A, B>.getOrElse(default: (A) -> B): B
/**
* Extract the Right value or return null
*/
fun <A, B> Either<A, B>.getOrNull(): B?
/**
* Extract the Left value or return null
*/
fun <A, B> Either<A, B>.leftOrNull(): A?
/**
* Convert Either to Option, keeping only Right values
*/
fun <A, B> Either<A, B>.getOrNone(): Option<B>Transform Either values while preserving the container structure.
/**
* Transform the Right value if present
*/
fun <A, B, C> Either<A, B>.map(f: (B) -> C): Either<A, C>
/**
* Transform the Left value if present
*/
fun <A, B, C> Either<A, B>.mapLeft(f: (A) -> C): Either<C, B>
/**
* Monadic bind - chain operations that can fail
*/
fun <A, B, C> Either<A, B>.flatMap(f: (B) -> Either<A, C>): Either<A, C>
/**
* Swap Left and Right positions
*/
fun <A, B> Either<A, B>.swap(): Either<B, A>
/**
* Flatten nested Either (for Either<A, Either<A, B>>)
*/
fun <A, B> Either<A, Either<A, B>>.flatten(): Either<A, B>
/**
* Merge Either<A, A> into A
*/
fun <A> Either<A, A>.merge(): APerform side effects based on Either state without changing the value.
/**
* Execute action if Either is Right, return original Either
*/
fun <A, B> Either<A, B>.onRight(action: (B) -> Unit): Either<A, B>
/**
* Execute action if Either is Left, return original Either
*/
fun <A, B> Either<A, B>.onLeft(action: (A) -> Unit): Either<A, B>Recover from errors or handle specific exception types.
/**
* Recover from Left values using Raise DSL
*/
fun <A, B, EE> Either<A, B>.recover(
recover: Raise<EE>.(A) -> B
): Either<EE, B>
/**
* Catch and handle specific exception types
*/
inline fun <E, reified T : Throwable, A> Either<E, A>.catch(
catch: Raise<E>.(T) -> A
): Either<E, A>Combine multiple Either values, accumulating errors instead of short-circuiting.
/**
* Combine 2 Either values, accumulating errors with custom combiner
*/
fun <E, A, B, Z> Either.Companion.zipOrAccumulate(
combine: (E, E) -> E,
a: Either<E, A>,
b: Either<E, B>,
transform: (A, B) -> Z
): Either<E, Z>
/**
* Combine 2 Either values, accumulating errors in NonEmptyList
*/
fun <E, A, B, Z> Either.Companion.zipOrAccumulate(
a: Either<E, A>,
b: Either<E, B>,
transform: (A, B) -> Z
): Either<NonEmptyList<E>, Z>
// Similar functions exist for 3-10 parametersUsage Example:
data class User(val name: String, val email: String, val age: Int)
fun validateName(name: String): Either<String, String> =
if (name.isNotBlank()) name.right() else "Name cannot be blank".left()
fun validateEmail(email: String): Either<String, String> =
if (email.contains("@")) email.right() else "Invalid email".left()
fun validateAge(age: Int): Either<String, Int> =
if (age >= 0) age.right() else "Age cannot be negative".left()
// Accumulate all validation errors
val userResult = Either.zipOrAccumulate(
validateName(""),
validateEmail("invalid-email"),
validateAge(-5)
) { name, email, age -> User(name, email, age) }
// Result: Either.Left(NonEmptyList("Name cannot be blank", "Invalid email", "Age cannot be negative"))Convert Either to other Arrow types.
/**
* Convert Either to EitherNel
*/
fun <A, B> Either<A, B>.toEitherNel(): EitherNel<A, B>
/**
* Convert Either to Ior
*/
fun <A, B> Either<A, B>.toIor(): Ior<A, B>Combine multiple Either values, accumulating errors instead of short-circuiting on the first failure.
/**
* Combine 2 Either values with custom error combination
*/
fun <E, A, B, Z> Either.Companion.zipOrAccumulate(
combine: (E, E) -> E,
a: Either<E, A>,
b: Either<E, B>,
transform: (A, B) -> Z
): Either<E, Z>
/**
* Combine 2 Either values accumulating errors in NonEmptyList
*/
fun <E, A, B, Z> Either.Companion.zipOrAccumulate(
a: Either<E, A>,
b: Either<E, B>,
transform: (A, B) -> Z
): Either<NonEmptyList<E>, Z>
/**
* Combine 3 Either values with custom error combination
*/
fun <E, A, B, C, Z> Either.Companion.zipOrAccumulate(
combine: (E, E) -> E,
a: Either<E, A>,
b: Either<E, B>,
c: Either<E, C>,
transform: (A, B, C) -> Z
): Either<E, Z>
/**
* Combine 3 Either values accumulating errors in NonEmptyList
*/
fun <E, A, B, C, Z> Either.Companion.zipOrAccumulate(
a: Either<E, A>,
b: Either<E, B>,
c: Either<E, C>,
transform: (A, B, C) -> Z
): Either<NonEmptyList<E>, Z>
// Additional overloads available for 4-10 parameters with both custom combine and NonEmptyList accumulation
/**
* Combine 2 EitherNel values accumulating errors
*/
fun <E, A, B, Z> Either.Companion.zipOrAccumulate(
a: EitherNel<E, A>,
b: EitherNel<E, B>,
transform: (A, B) -> Z
): EitherNel<E, Z>
// Additional EitherNel overloads available for 3-10 parametersUsage Examples:
// Custom error combination
val result1 = Either.zipOrAccumulate(
{ e1, e2 -> "$e1; $e2" },
parseAge("invalid"),
parseName(""),
::User
) // Either.Left("Invalid age; Name required")
// Error accumulation with NonEmptyList
val result2 = Either.zipOrAccumulate(
validateEmail("invalid-email"),
validateAge("-5"),
validateName("")
) { email, age, name -> User(email, age, name) }
// Either.Left(NonEmptyList("Invalid email", "Age must be positive", "Name required"))/**
* Either with NonEmptyList for error accumulation
*/
typealias EitherNel<E, A> = Either<NonEmptyList<E>, A>Install with Tessl CLI
npx tessl i tessl/maven-io-arrow-kt--arrow-core