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

safe-collections.mddocs/

Safe Collections

NonEmptyList provides collections guaranteed to have at least one element, eliminating the possibility of empty collection errors and enabling safe head/tail operations. It implements the List interface while ensuring non-emptiness.

Core Types

// Value class for performance
value class NonEmptyList<out E>(val all: List<E>) : List<E>, NonEmptyCollection<E>

// Type alias
typealias Nel<A> = NonEmptyList<A>

Construction

// Primary constructor
NonEmptyList(head: E, tail: List<E>)

// From varargs
fun <T> nonEmptyListOf(head: T, vararg tail: T): NonEmptyList<T>

// Safe conversion from List
fun <T> List<T>.toNonEmptyListOrNull(): NonEmptyList<T>?

// From single element
fun <A> A.nel(): NonEmptyList<A>

Safe Access

// Always safe - never throws
val head: E
val tail: List<E>

// Convert back to regular List
fun toList(): List<E>

// Always returns false
override fun isEmpty(): Boolean

// Safe last element (never null)
override fun lastOrNull(): E

Transformation

// Transform elements (preserves non-emptiness)
override fun <T> map(transform: (E) -> T): NonEmptyList<T>

// Monadic bind
override fun <T> flatMap(transform: (E) -> NonEmptyCollection<T>): NonEmptyList<T>

// Transform with index
override fun <T> mapIndexed(transform: (index: Int, E) -> T): NonEmptyList<T>

// Remove duplicates
override fun distinct(): NonEmptyList<E>
override fun <K> distinctBy(selector: (E) -> K): NonEmptyList<E>

Combining Lists

// Concatenation (always non-empty result)
operator fun plus(l: NonEmptyList<E>): NonEmptyList<E>
override operator fun plus(elements: Iterable<E>): NonEmptyList<E>
override operator fun plus(element: E): NonEmptyList<E>

Folding Operations

// Left fold with guaranteed execution (AT_LEAST_ONCE)
fun <Acc> foldLeft(b: Acc, f: (Acc, E) -> Acc): Acc

Comonadic Operations

// Comonadic operations
fun <T> coflatMap(f: (NonEmptyList<E>) -> T): NonEmptyList<T>
fun extract(): E // Returns head

Zipping Operations

// Zip two NonEmptyLists
fun <T> zip(other: NonEmptyList<T>): NonEmptyList<Pair<E, T>>

// Zip with transform function (2-5 arity overloads)
fun <B, Z> zip(
    b: NonEmptyList<B>,
    map: (E, B) -> Z
): NonEmptyList<Z>

// Zip with padding (handles different lengths)
fun <T> padZip(other: NonEmptyList<T>): NonEmptyList<Pair<E?, T?>>
fun <B, C> padZip(
    other: NonEmptyList<B>, 
    left: (E) -> C, 
    right: (B) -> C, 
    both: (E, B) -> C
): NonEmptyList<C>

Alignment Operations

// Align with another list using Ior
fun <T> align(other: NonEmptyList<T>): NonEmptyList<Ior<E, T>>

Usage Examples

Basic Construction and Access

import arrow.core.*

// Create NonEmptyList
val users = nonEmptyListOf("Alice", "Bob", "Charlie")
val singleUser = "Alice".nel()

// Safe access (never throws)
println(users.head)  // "Alice"
println(users.tail)  // ["Bob", "Charlie"]

// Convert to regular List when needed
val regularList: List<String> = users.toList()

Safe Operations

import arrow.core.*

// Transform elements
val lengths = users.map { it.length }  // NonEmptyList(5, 3, 7)

// Filter-like operations with safe conversion
val longNames = users.filter { it.length > 4 }.toNonEmptyListOrNull()
// Option: Some(NonEmptyList("Alice", "Charlie")) or None if empty

// Chain operations safely
val processed = users
    .map { it.uppercase() }
    .map { "Hello, $it!" }
    .distinct()

Combining Collections

import arrow.core.*

val moreUsers = nonEmptyListOf("David", "Eve")
val allUsers = users + moreUsers  // NonEmptyList("Alice", "Bob", "Charlie", "David", "Eve")

// Add single elements
val withNewUser = users + "Frank"  // NonEmptyList("Alice", "Bob", "Charlie", "Frank")

Folding Operations

import arrow.core.*

val numbers = nonEmptyListOf(1, 2, 3, 4, 5)

// Left fold - guaranteed to execute at least once
val sum = numbers.foldLeft(0) { acc, n -> acc + n }  // 15

// Build strings
val concatenated = users.foldLeft("Users: ") { acc, user -> "$acc$user, " }
// "Users: Alice, Bob, Charlie, "

Zipping Operations

import arrow.core.*

val names = nonEmptyListOf("Alice", "Bob", "Charlie")
val ages = nonEmptyListOf(25, 30, 35)
val cities = nonEmptyListOf("NYC", "SF")

// Zip two lists (truncates to shorter length)
val pairs = names.zip(ages)  // NonEmptyList(("Alice", 25), ("Bob", 30), ("Charlie", 35))

// Zip with transform
val descriptions = names.zip(ages) { name, age -> "$name is $age years old" }
// NonEmptyList("Alice is 25 years old", "Bob is 30 years old", ...)

// Pad zip (handles different lengths)
val padded = names.padZip(cities)
// NonEmptyList(("Alice", "NYC"), ("Bob", "SF"), ("Charlie", null))

Advanced Operations

import arrow.core.*

// Comonadic operations
val duplicated = names.coflatMap { nel -> 
    "${nel.head} (from list of ${nel.size})"
}
// NonEmptyList("Alice (from list of 3)", "Bob (from list of 2)", "Charlie (from list of 1)")

// Alignment with Ior
val aligned = names.align(ages)
// Each element is Ior<String, Int> representing the alignment

Safe Conversion Patterns

import arrow.core.*

// Safe conversion from List
fun processUsers(userList: List<String>): String =
    userList.toNonEmptyListOrNull()
        ?.let { nel -> "Processing ${nel.size} users starting with ${nel.head}" }
        ?: "No users to process"

val emptyResult = processUsers(emptyList())      // "No users to process"
val validResult = processUsers(listOf("Alice"))  // "Processing 1 users starting with Alice"

Integration with Other Arrow Types

import arrow.core.*

// With Option
val firstLongName: Option<String> = users
    .filter { it.length > 4 }
    .toNonEmptyListOrNull()
    .toOption()
    .map { it.head }

// With Either for validation
fun validateUsers(input: List<String>): Either<String, NonEmptyList<String>> =
    input.toNonEmptyListOrNull()
        ?.right()
        ?: "User list cannot be empty".left()

Performance Considerations

import arrow.core.*

// NonEmptyList is a value class - no runtime overhead
val efficient = nonEmptyListOf(1, 2, 3)  // Wraps List<Int> at compile time

// Delegation to List operations when possible
val sorted = efficient.sorted()           // Uses List.sorted() internally
val reversed = efficient.reversed()       // Uses List.reversed() internally

NonEmptySet Extensions

NonEmptySet provides similar functionality to NonEmptyList but ensures uniqueness of elements.

NonEmptySet Construction

// Primary constructor
NonEmptySet(first: E, rest: Iterable<E>)

// From varargs
fun <T> nonEmptySetOf(first: T, vararg rest: T): NonEmptySet<T>

// Safe conversion from Iterable
fun <T> Iterable<T>.toNonEmptySetOrNull(): NonEmptySet<T>?
fun <T> Iterable<T>.toNonEmptySetOrNone(): Option<NonEmptySet<T>>
fun <T> Iterable<T>.toNonEmptySetOrThrow(): NonEmptySet<T>

// Performance optimized wrapping (unsafe - for performance)
fun <T> Set<T>.wrapAsNonEmptySetOrNull(): NonEmptySet<T>?
fun <T> Set<T>.wrapAsNonEmptySetOrThrow(): NonEmptySet<T>

NonEmptySet Error Accumulation

// Error accumulation with custom combine function
fun <Error, E, T> NonEmptySet<E>.mapOrAccumulate(
    combine: (Error, Error) -> Error,
    transform: RaiseAccumulate<Error>.(E) -> T
): Either<Error, NonEmptySet<T>>

// Error accumulation with NonEmptyList errors
fun <Error, E, T> NonEmptySet<E>.mapOrAccumulate(
    transform: RaiseAccumulate<Error>.(E) -> T
): Either<NonEmptyList<Error>, NonEmptySet<T>>

NonEmptySet Usage Examples

import arrow.core.*

// Create unique collections
val duplicateList = listOf("A", "B", "A", "C", "B")
val uniqueSet = duplicateList.toNonEmptySetOrNull()  // NonEmptySet("A", "B", "C")

// Safe conversion patterns
val maybeUniqueData = inputData.toNonEmptySetOrNone()
when (maybeUniqueData) {
    is Some -> println("Found ${maybeUniqueData.value.size} unique items")
    is None -> println("No data provided")
}

// Convert between types
val fromList = nonEmptyListOf(1, 2, 3, 2, 1)
val asSet = fromList.toNonEmptySet()  // NonEmptySet(1, 2, 3)
val backToList = asSet.toNonEmptyList()  // NonEmptyList(1, 2, 3) - order may vary

// Error accumulation example
import arrow.core.raise.*

data class ValidationError(val field: String, val message: String)

fun Raise<ValidationError>.validateEmail(email: String): String {
    ensure(email.contains('@')) { ValidationError("email", "Invalid format") }
    return email
}

val emails = nonEmptySetOf("alice@test.com", "invalid-email", "bob@test.com")
val validatedEmails = emails.mapOrAccumulate { email ->
    validateEmail(email)
}

when (validatedEmails) {
    is Either.Right -> println("All emails valid: ${validatedEmails.value}")
    is Either.Left -> println("Validation errors: ${validatedEmails.value}")
}

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