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

collection-extensions.mddocs/

Collection Extensions

Arrow Core provides functional programming extensions for Kotlin collections, focusing on zip operations, alignment operations, error handling, and utility functions.

Capabilities

Zip Operations

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

Map with Error Accumulation

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

Alignment Operations

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

Collection Analysis and Utilities

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 Element Access

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)  // None

Reduce Operations

Enhanced 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?

Option and Comparison Utilities

/**
 * 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>

Sequence Extensions

Arrow Core also provides similar extensions for Sequence with lazy evaluation.

Sequence Zip Operations

/**
 * 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>

Sequence Alignment and Padding

/**
 * 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?>>

Sequence Utilities

/**
 * 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>>

Map Extensions

Enhanced operations for Map collections.

Map Zip Operations

/**
 * 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>

Map Alignment and Padding

/**
 * 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>

Map Utilities

/**
 * 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 types

Install with Tessl CLI

npx tessl i tessl/maven-io-arrow-kt--arrow-core-iosarm64@2.1.2

docs

collection-extensions.md

core-data-types.md

effect-system.md

index.md

raise-dsl.md

tuple-types.md

tile.json