Functional companion to Kotlin's Standard Library providing type-safe error handling and functional programming constructs for iOS x64 target
—
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.
// Value class for performance
value class NonEmptyList<out E>(val all: List<E>) : List<E>, NonEmptyCollection<E>
// Type alias
typealias Nel<A> = NonEmptyList<A>// 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>// 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// 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>// 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>// Left fold with guaranteed execution (AT_LEAST_ONCE)
fun <Acc> foldLeft(b: Acc, f: (Acc, E) -> Acc): Acc// Comonadic operations
fun <T> coflatMap(f: (NonEmptyList<E>) -> T): NonEmptyList<T>
fun extract(): E // Returns head// 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>// Align with another list using Ior
fun <T> align(other: NonEmptyList<T>): NonEmptyList<Ior<E, T>>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()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()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")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, "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))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 alignmentimport 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"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()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() internallyNonEmptySet provides similar functionality to NonEmptyList but ensures uniqueness of elements.
// 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>// 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>>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