Functional companion to Kotlin's Standard Library - Core module with iosArm64 target support
—
Arrow Core provides functional programming extensions for Kotlin collections, focusing on zip operations, alignment operations, error handling, and utility functions.
Enhanced zip operations that support multiple collections and custom transformations.
/**
* Zip with 3 collections using a transform function
*/
inline fun <B, C, D, E> Iterable<B>.zip(
c: Iterable<C>,
d: Iterable<D>,
transform: (B, C, D) -> E
): List<E>
/**
* Zip with 4 collections using a transform function
*/
inline fun <B, C, D, E, F> Iterable<B>.zip(
c: Iterable<C>,
d: Iterable<D>,
e: Iterable<E>,
transform: (B, C, D, E) -> F
): List<F>
/**
* Zip operations available up to 10 collections
*/Usage Examples:
import arrow.core.*
// Zip three collections
val numbers = listOf(1, 2, 3)
val letters = listOf("a", "b", "c")
val symbols = listOf("!", "?", ".")
val combined = numbers.zip(letters, symbols) { num, letter, symbol ->
"$num$letter$symbol"
}
// ["1a!", "2b?", "3c."]
// Zip four collections
val list1 = listOf(1, 2)
val list2 = listOf("a", "b")
val list3 = listOf(true, false)
val list4 = listOf(10.0, 20.0)
val result = list1.zip(list2, list3, list4) { num, str, bool, dbl ->
Triple(num + str, bool, dbl)
}Transform collections while accumulating errors instead of short-circuiting on the first error.
/**
* Map over collection with error accumulation using custom error combination
*/
inline fun <Error, A, B> Iterable<A>.mapOrAccumulate(
combine: (Error, Error) -> Error,
transform: RaiseAccumulate<Error>.(A) -> B
): Either<Error, List<B>>
/**
* Map over collection with error accumulation using NonEmptyList
*/
inline fun <Error, A, B> Iterable<A>.mapOrAccumulate(
transform: RaiseAccumulate<Error>.(A) -> B
): Either<NonEmptyList<Error>, List<B>>
/**
* Flatten collection of Either values with error accumulation
*/
inline fun <Error, A> Iterable<Either<Error, A>>.flattenOrAccumulate(
combine: (Error, Error) -> Error
): Either<Error, List<A>>
/**
* Flatten collection of Either values to NonEmptyList of errors
*/
fun <Error, A> Iterable<Either<Error, A>>.flattenOrAccumulate(): Either<NonEmptyList<Error>, List<A>>Usage Examples:
import arrow.core.*
import arrow.core.raise.*
// Map with error accumulation
val inputs = listOf("1", "2", "invalid1", "4", "invalid2")
val results = inputs.mapOrAccumulate { str ->
str.toIntOrNull() ?: raise("Invalid number: $str")
}
// Left(NonEmptyList("Invalid number: invalid1", "Invalid number: invalid2"))
// Custom error combination
val customResults = inputs.mapOrAccumulate(
combine = { e1, e2 -> "$e1; $e2" }
) { str ->
str.toIntOrNull() ?: raise("Invalid: $str")
}
// Left("Invalid: invalid1; Invalid: invalid2")
// Flatten Either collection
val eithers = listOf(1.right(), "error1".left(), 3.right(), "error2".left())
val flattened = eithers.flattenOrAccumulate()
// Left(NonEmptyList("error1", "error2"))Combine collections that may have different lengths, handling missing elements gracefully.
/**
* Align two collections using Ior to represent presence
*/
fun <A, B> Iterable<A>.align(b: Iterable<B>): List<Ior<A, B>>
/**
* Align and transform two collections
*/
inline fun <A, B, C> Iterable<A>.align(
b: Iterable<B>,
fa: (Ior<A, B>) -> C
): List<C>
/**
* Pad zip - zip with null padding for missing elements
*/
fun <A, B> Iterable<A>.padZip(other: Iterable<B>): List<Pair<A?, B?>>
/**
* Pad zip with transformation
*/
inline fun <A, B, C> Iterable<A>.padZip(
other: Iterable<B>,
fa: (A?, B?) -> C
): List<C>
/**
* Left pad zip - keep all elements from right collection
*/
fun <A, B> Iterable<A>.leftPadZip(other: Iterable<B>): List<Pair<A?, B>>
/**
* Left pad zip with transformation
*/
inline fun <A, B, C> Iterable<A>.leftPadZip(
other: Iterable<B>,
fab: (A?, B) -> C
): List<C>
/**
* Right pad zip - keep all elements from left collection
*/
fun <A, B> Iterable<A>.rightPadZip(other: Iterable<B>): List<Pair<A, B?>>
/**
* Right pad zip with transformation
*/
inline fun <A, B, C> Iterable<A>.rightPadZip(
other: Iterable<B>,
fa: (A, B?) -> C
): List<C>Usage Examples:
import arrow.core.*
// Align collections of different lengths
val left = listOf(1, 2, 3)
val right = listOf("a", "b")
val aligned = left.align(right)
// [Ior.Both(1, "a"), Ior.Both(2, "b"), Ior.Left(3)]
// Align with transformation
val combined = left.align(right) { ior ->
ior.fold(
{ "Left: $it" },
{ "Right: $it" },
{ l, r -> "Both: $l, $r" }
)
}
// ["Both: 1, a", "Both: 2, b", "Left: 3"]
// Pad zip with nulls
val padded = listOf(1, 2).padZip(listOf("a"))
// [Pair(1, "a"), Pair(2, null)]
// Left pad zip
val leftPadded = listOf(1).leftPadZip(listOf("a", "b"))
// [Pair(1, "a"), Pair(null, "b")]
// Right pad zip
val rightPadded = listOf(1, 2).rightPadZip(listOf("a"))
// [Pair(1, "a"), Pair(2, null)]Utility functions for collection processing and analysis.
/**
* Split collection into head and tail
*/
fun <A> Iterable<A>.split(): Pair<List<A>, A>?
/**
* Get tail (all but first element)
*/
fun <A> Iterable<A>.tail(): List<A>
/**
* Interleave elements with another collection
*/
fun <A> Iterable<A>.interleave(other: Iterable<A>): List<A>
/**
* Unweave using a function that produces iterables
*/
fun <A, B> Iterable<A>.unweave(ffa: (A) -> Iterable<B>): List<B>
/**
* Separate Either values into lefts and rights
*/
fun <A, B> Iterable<Either<A, B>>.separateEither(): Pair<List<A>, List<B>>
/**
* Separate Either values with transformation
*/
inline fun <T, A, B> Iterable<T>.separateEither(
f: (T) -> Either<A, B>
): Pair<List<A>, List<B>>
/**
* Separate Ior values into components
*/
fun <A, B> Iterable<Ior<A, B>>.separateIor(): Pair<List<A>, List<B>>
/**
* Unalign Ior values with nulls for missing sides
*/
fun <A, B> Iterable<Ior<A, B>>.unalign(): Pair<List<A?>, List<B?>>
/**
* Unalign with transformation to Ior
*/
inline fun <A, B, C> Iterable<C>.unalign(
fa: (C) -> Ior<A, B>
): Pair<List<A?>, List<B?>>Usage Examples:
import arrow.core.*
// Split into head and tail
val numbers = listOf(1, 2, 3, 4)
val split = numbers.split()
// Pair([2, 3, 4], 1)
// Interleave collections
val evens = listOf(2, 4, 6)
val odds = listOf(1, 3, 5, 7)
val interleaved = evens.interleave(odds)
// [2, 1, 4, 3, 6, 5, 7]
// Separate Either values
val eithers = listOf(1.left(), 2.right(), 3.left(), 4.right())
val (lefts, rights) = eithers.separateEither()
// lefts: [1, 3], rights: [2, 4]
// Separate with transformation
val numbers2 = listOf(1, 2, 3, 4, 5, 6)
val (evens2, odds2) = numbers2.separateEither { num ->
if (num % 2 == 0) num.left() else num.right()
}
// evens2: [2, 4, 6], odds2: [1, 3, 5]
// Separate Ior values
val iors = listOf(
Ior.Both("a", 1),
Ior.Left("b"),
Ior.Right(2)
)
val (leftValues, rightValues) = iors.separateIor()
// leftValues: ["a", "b"], rightValues: [1, 2]Safe alternatives to collection access operations that return Option instead of throwing exceptions.
/**
* Get first element safely
*/
fun <T> Iterable<T>.firstOrNone(): Option<T>
/**
* Get first element matching predicate safely
*/
inline fun <T> Iterable<T>.firstOrNone(predicate: (T) -> Boolean): Option<T>
/**
* Get single element safely
*/
fun <T> Iterable<T>.singleOrNone(): Option<T>
/**
* Get single element matching predicate safely
*/
inline fun <T> Iterable<T>.singleOrNone(predicate: (T) -> Boolean): Option<T>
/**
* Get last element safely
*/
fun <T> Iterable<T>.lastOrNone(): Option<T>
/**
* Get last element matching predicate safely
*/
inline fun <T> Iterable<T>.lastOrNone(predicate: (T) -> Boolean): Option<T>
/**
* Get element at index safely
*/
fun <T> Iterable<T>.elementAtOrNone(index: Int): Option<T>Usage Examples:
import arrow.core.*
// Safe first element access
val numbers = listOf(1, 2, 3)
val first = numbers.firstOrNone() // Some(1)
val empty = emptyList<Int>().firstOrNone() // None
// Safe element access with predicate
val even = numbers.firstOrNone { it % 2 == 0 } // Some(2)
val notFound = numbers.firstOrNone { it > 10 } // None
// Safe single element access
val single = listOf(42).singleOrNone() // Some(42)
val multiple = listOf(1, 2).singleOrNone() // None
// Safe element at index
val atIndex = numbers.elementAtOrNone(1) // Some(2)
val outOfBounds = numbers.elementAtOrNone(10) // NoneEnhanced reduce operations with proper handling of empty collections.
/**
* Reduce with initial value function, returns null for empty collections
*/
inline fun <A, B> Iterable<A>.reduceOrNull(
initial: (A) -> B,
operation: (acc: B, A) -> B
): B?
/**
* Right reduce with proper handling of empty collections
*/
inline fun <A, B> List<A>.reduceRightNull(
initial: (A) -> B,
operation: (A, acc: B) -> B
): B?/**
* Filter Option values, keeping only Some values
*/
fun <T> Iterable<Option<T>>.filterOption(): List<T>
/**
* Alias for filterOption
*/
fun <T> Iterable<Option<T>>.flattenOption(): List<T>
/**
* Prepend element to iterable
*/
infix fun <T> T.prependTo(list: Iterable<T>): List<T>
/**
* Compare iterables lexicographically
*/
operator fun <A : Comparable<A>> Iterable<A>.compareTo(other: Iterable<A>): Int
/**
* Crosswalk operation
*/
fun <A, B> Iterable<A>.crosswalk(f: (A) -> Iterable<B>): List<List<B>>
/**
* Crosswalk with Map result
*/
fun <A, K, V> Iterable<A>.crosswalkMap(f: (A) -> Map<K, V>): Map<K, List<V>>
/**
* Crosswalk with nullable result
*/
fun <A, B> Iterable<A>.crosswalkNull(f: (A) -> B?): List<B>?
/**
* Flatten nested iterables
*/
fun <A> Iterable<Iterable<A>>.flatten(): List<A>Arrow Core also provides similar extensions for Sequence with lazy evaluation.
/**
* Zip sequences with 3-10 parameters, maintaining lazy evaluation
*/
fun <B, C, D, E> Sequence<B>.zip(
c: Sequence<C>,
d: Sequence<D>,
map: (B, C, D) -> E
): Sequence<E>/**
* Align two sequences with Ior
*/
fun <A, B> Sequence<A>.align(seqB: Sequence<B>): Sequence<Ior<A, B>>
/**
* Pad zip sequences with nulls
*/
fun <A, B> Sequence<A>.padZip(other: Sequence<B>): Sequence<Pair<A?, B?>>
/**
* Left pad zip sequences
*/
fun <A, B> Sequence<A>.leftPadZip(other: Sequence<B>): Sequence<Pair<A?, B>>
/**
* Right pad zip sequences
*/
fun <A, B> Sequence<A>.rightPadZip(other: Sequence<B>): Sequence<Pair<A, B?>>/**
* Split sequence into head and tail
*/
fun <A> Sequence<A>.split(): Pair<Sequence<A>, A>?
/**
* Interleave sequences
*/
fun <A> Sequence<A>.interleave(other: Sequence<A>): Sequence<A>
/**
* Filter Option values in sequence
*/
fun <A> Sequence<Option<A>>.filterOption(): Sequence<A>
/**
* Separate Either values in sequence
*/
fun <A, B> Sequence<Either<A, B>>.separateEither(): Pair<List<A>, List<B>>
/**
* Map with error accumulation for sequences
*/
fun <Error, A, B> Sequence<A>.mapOrAccumulate(
combine: (Error, Error) -> Error,
transform: RaiseAccumulate<Error>.(A) -> B
): Either<Error, List<B>>Enhanced operations for Map collections.
/**
* Zip maps by key intersection
*/
fun <K, A, B> Map<K, A>.zip(other: Map<K, B>): Map<K, Pair<A, B>>
/**
* Zip maps with transformation (up to 10 maps)
*/
inline fun <Key, A, B, C> Map<Key, A>.zip(
other: Map<Key, B>,
map: (Key, A, B) -> C
): Map<Key, C>/**
* Align maps using Ior
*/
fun <K, A, B> Map<K, A>.align(b: Map<K, B>): Map<K, Ior<A, B>>
/**
* Pad zip maps
*/
fun <K, A, B> Map<K, A>.padZip(other: Map<K, B>): Map<K, Pair<A?, B?>>
/**
* Structural align with combination function
*/
fun <K, A> Map<K, A>.salign(
other: Map<K, A>,
combine: (A, A) -> A
): Map<K, A>/**
* FlatMap values keeping only matching keys
*/
fun <K, A, B> Map<K, A>.flatMapValues(f: (Map.Entry<K, A>) -> Map<K, B>): Map<K, B>
/**
* Map values with error accumulation
*/
inline fun <K, E, A, B> Map<K, A>.mapValuesOrAccumulate(
combine: (E, E) -> E,
transform: RaiseAccumulate<E>.(Map.Entry<K, A>) -> B
): Either<E, Map<K, B>>
/**
* Safe get operation returning Option
*/
fun <K, V> Map<K, V>.getOrNone(key: K): Option<V>
/**
* Combine maps with function for conflicting keys
*/
fun <K, A> Map<K, A>.combine(
other: Map<K, A>,
combine: (A, A) -> A
): Map<K, A>
/**
* Filter Option values in map
*/
fun <K, A> Map<K, Option<A>>.filterOption(): Map<K, A>
/**
* Unalign map of Ior values
*/
fun <K, A, B> Map<K, Ior<A, B>>.unalign(): Pair<Map<K, A>, Map<K, B>>
/**
* Unzip map of pairs
*/
fun <K, A, B> Map<K, Pair<A, B>>.unzip(): Pair<Map<K, A>, Map<K, B>>Usage Examples:
import arrow.core.*
// Map zip by intersection
val map1 = mapOf(1 to "a", 2 to "b", 3 to "c")
val map2 = mapOf(1 to 10, 2 to 20, 4 to 40)
val zipped = map1.zip(map2)
// {1=(a, 10), 2=(b, 20)}
// Map alignment
val aligned = map1.align(map2)
// {1=Ior.Both(a, 10), 2=Ior.Both(b, 20), 3=Ior.Left(c), 4=Ior.Right(40)}
// Safe get
val value = map1.getOrNone(1) // Some("a")
val missing = map1.getOrNone(5) // None
// Combine maps
val combined = map1.combine(map2) { str, num -> "$str$num" }
// Only works if both maps have same value typesInstall with Tessl CLI
npx tessl i tessl/maven-io-arrow-kt--arrow-core-iosarm64@2.1.2