Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce NonEmptyCollection #3068

Merged
merged 8 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 75 additions & 10 deletions arrow-libs/core/arrow-core/api/arrow-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -776,34 +776,74 @@ public final class arrow/core/Memoization {
public static final fun memoize (Lkotlin/jvm/functions/Function5;)Lkotlin/jvm/functions/Function5;
}

public final class arrow/core/NonEmptyList : kotlin/collections/AbstractList {
public abstract interface class arrow/core/NonEmptyCollection : java/util/Collection, kotlin/jvm/internal/markers/KMappedMarker {
public abstract fun distinct ()Larrow/core/NonEmptyList;
public abstract fun distinctBy (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public abstract fun firstOrNull ()Ljava/lang/Object;
public abstract fun flatMap (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public abstract fun getHead ()Ljava/lang/Object;
public abstract fun isEmpty ()Z
public abstract fun lastOrNull ()Ljava/lang/Object;
public abstract fun map (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public abstract fun mapIndexed (Lkotlin/jvm/functions/Function2;)Larrow/core/NonEmptyList;
public abstract fun plus (Ljava/lang/Iterable;)Larrow/core/NonEmptyCollection;
public abstract fun plus (Ljava/lang/Object;)Larrow/core/NonEmptyCollection;
public abstract fun toNonEmptyList ()Larrow/core/NonEmptyList;
public abstract fun toNonEmptySet-5sCjGKo ()Ljava/util/Set;
public abstract fun zip (Larrow/core/NonEmptyCollection;)Larrow/core/NonEmptyCollection;
}

public final class arrow/core/NonEmptyCollection$DefaultImpls {
public static fun distinct (Larrow/core/NonEmptyCollection;)Larrow/core/NonEmptyList;
public static fun distinctBy (Larrow/core/NonEmptyCollection;Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public static fun firstOrNull (Larrow/core/NonEmptyCollection;)Ljava/lang/Object;
public static fun flatMap (Larrow/core/NonEmptyCollection;Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public static fun isEmpty (Larrow/core/NonEmptyCollection;)Z
public static fun map (Larrow/core/NonEmptyCollection;Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public static fun mapIndexed (Larrow/core/NonEmptyCollection;Lkotlin/jvm/functions/Function2;)Larrow/core/NonEmptyList;
public static fun toNonEmptyList (Larrow/core/NonEmptyCollection;)Larrow/core/NonEmptyList;
public static fun toNonEmptySet-5sCjGKo (Larrow/core/NonEmptyCollection;)Ljava/util/Set;
public static fun zip (Larrow/core/NonEmptyCollection;Larrow/core/NonEmptyCollection;)Larrow/core/NonEmptyCollection;
}

public final class arrow/core/NonEmptyList : kotlin/collections/AbstractList, arrow/core/NonEmptyCollection {
public static final field Companion Larrow/core/NonEmptyList$Companion;
public fun <init> (Ljava/lang/Object;Ljava/util/List;)V
public synthetic fun <init> (Ljava/util/List;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun align (Larrow/core/NonEmptyList;)Larrow/core/NonEmptyList;
public final fun coflatMap (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun distinct ()Larrow/core/NonEmptyList;
public fun distinctBy (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun equals (Ljava/lang/Object;)Z
public final fun extract ()Ljava/lang/Object;
public final fun flatMap (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun firstOrNull ()Ljava/lang/Object;
public fun flatMap (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public final fun foldLeft (Ljava/lang/Object;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object;
public static final fun fromList (Ljava/util/List;)Larrow/core/Option;
public static final fun fromListUnsafe (Ljava/util/List;)Larrow/core/NonEmptyList;
public fun get (I)Ljava/lang/Object;
public final fun getAll ()Ljava/util/List;
public final fun getHead ()Ljava/lang/Object;
public fun getHead ()Ljava/lang/Object;
public fun getSize ()I
public final fun getTail ()Ljava/util/List;
public fun hashCode ()I
public fun isEmpty ()Z
public final fun map (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun lastOrNull ()Ljava/lang/Object;
public fun map (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun mapIndexed (Lkotlin/jvm/functions/Function2;)Larrow/core/NonEmptyList;
public final fun padZip (Larrow/core/NonEmptyList;)Larrow/core/NonEmptyList;
public final fun padZip (Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Larrow/core/NonEmptyList;
public final fun plus (Larrow/core/NonEmptyList;)Larrow/core/NonEmptyList;
public final fun plus (Ljava/lang/Object;)Larrow/core/NonEmptyList;
public final fun plus (Ljava/util/List;)Larrow/core/NonEmptyList;
public synthetic fun plus (Ljava/lang/Iterable;)Larrow/core/NonEmptyCollection;
public fun plus (Ljava/lang/Iterable;)Larrow/core/NonEmptyList;
public synthetic fun plus (Ljava/lang/Object;)Larrow/core/NonEmptyCollection;
public fun plus (Ljava/lang/Object;)Larrow/core/NonEmptyList;
public final fun salign (Larrow/typeclasses/Semigroup;Larrow/core/NonEmptyList;)Larrow/core/NonEmptyList;
public final fun toList ()Ljava/util/List;
public fun toNonEmptyList ()Larrow/core/NonEmptyList;
public fun toNonEmptySet-5sCjGKo ()Ljava/util/Set;
public fun toString ()Ljava/lang/String;
public fun zip (Larrow/core/NonEmptyCollection;)Larrow/core/NonEmptyCollection;
public final fun zip (Larrow/core/NonEmptyList;)Larrow/core/NonEmptyList;
public final fun zip (Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function10;)Larrow/core/NonEmptyList;
public final fun zip (Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function9;)Larrow/core/NonEmptyList;
Expand Down Expand Up @@ -851,7 +891,7 @@ public final class arrow/core/NonEmptyListKt {
public static final fun unzip (Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair;
}

public final class arrow/core/NonEmptySet : java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
public final class arrow/core/NonEmptySet : arrow/core/NonEmptyCollection, java/util/Set, kotlin/jvm/internal/markers/KMappedMarker {
public fun add (Ljava/lang/Object;)Z
public fun addAll (Ljava/util/Collection;)Z
public static final synthetic fun box-impl (Ljava/util/Set;)Larrow/core/NonEmptySet;
Expand All @@ -861,9 +901,19 @@ public final class arrow/core/NonEmptySet : java/util/Set, kotlin/jvm/internal/m
public static fun contains-impl (Ljava/util/Set;Ljava/lang/Object;)Z
public fun containsAll (Ljava/util/Collection;)Z
public static fun containsAll-impl (Ljava/util/Set;Ljava/util/Collection;)Z
public fun distinct ()Larrow/core/NonEmptyList;
public static fun distinct-impl (Ljava/util/Set;)Larrow/core/NonEmptyList;
public fun distinctBy (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public static fun distinctBy-impl (Ljava/util/Set;Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun equals (Ljava/lang/Object;)Z
public static fun equals-impl (Ljava/util/Set;Ljava/lang/Object;)Z
public static final fun equals-impl0 (Ljava/util/Set;Ljava/util/Set;)Z
public fun firstOrNull ()Ljava/lang/Object;
public static fun firstOrNull-impl (Ljava/util/Set;)Ljava/lang/Object;
public fun flatMap (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public static fun flatMap-impl (Ljava/util/Set;Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun getHead ()Ljava/lang/Object;
public static fun getHead-impl (Ljava/util/Set;)Ljava/lang/Object;
public fun getSize ()I
public static fun getSize-impl (Ljava/util/Set;)I
public fun hashCode ()I
Expand All @@ -872,18 +922,33 @@ public final class arrow/core/NonEmptySet : java/util/Set, kotlin/jvm/internal/m
public static fun isEmpty-impl (Ljava/util/Set;)Z
public fun iterator ()Ljava/util/Iterator;
public static fun iterator-impl (Ljava/util/Set;)Ljava/util/Iterator;
public static final fun map-J9TPrxk (Ljava/util/Set;Lkotlin/jvm/functions/Function1;)Ljava/util/Set;
public static final fun plus-J9TPrxk (Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set;
public static final fun plus-J9TPrxk (Ljava/util/Set;Ljava/util/Set;)Ljava/util/Set;
public fun lastOrNull ()Ljava/lang/Object;
public static fun lastOrNull-impl (Ljava/util/Set;)Ljava/lang/Object;
public fun map (Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public static fun map-impl (Ljava/util/Set;Lkotlin/jvm/functions/Function1;)Larrow/core/NonEmptyList;
public fun mapIndexed (Lkotlin/jvm/functions/Function2;)Larrow/core/NonEmptyList;
public static fun mapIndexed-impl (Ljava/util/Set;Lkotlin/jvm/functions/Function2;)Larrow/core/NonEmptyList;
public synthetic fun plus (Ljava/lang/Iterable;)Larrow/core/NonEmptyCollection;
public synthetic fun plus (Ljava/lang/Object;)Larrow/core/NonEmptyCollection;
public fun plus-J9TPrxk (Ljava/lang/Iterable;)Ljava/util/Set;
public fun plus-J9TPrxk (Ljava/lang/Object;)Ljava/util/Set;
public static fun plus-J9TPrxk (Ljava/util/Set;Ljava/lang/Iterable;)Ljava/util/Set;
public static fun plus-J9TPrxk (Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set;
public fun remove (Ljava/lang/Object;)Z
public fun removeAll (Ljava/util/Collection;)Z
public fun retainAll (Ljava/util/Collection;)Z
public synthetic fun size ()I
public fun toArray ()[Ljava/lang/Object;
public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object;
public fun toNonEmptyList ()Larrow/core/NonEmptyList;
public static fun toNonEmptyList-impl (Ljava/util/Set;)Larrow/core/NonEmptyList;
public fun toNonEmptySet-5sCjGKo ()Ljava/util/Set;
public static fun toNonEmptySet-5sCjGKo (Ljava/util/Set;)Ljava/util/Set;
public fun toString ()Ljava/lang/String;
public static fun toString-impl (Ljava/util/Set;)Ljava/lang/String;
public final synthetic fun unbox-impl ()Ljava/util/Set;
public fun zip (Larrow/core/NonEmptyCollection;)Larrow/core/NonEmptyCollection;
public static fun zip-impl (Ljava/util/Set;Larrow/core/NonEmptyCollection;)Larrow/core/NonEmptyCollection;
}

public final class arrow/core/NonEmptySetKt {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package arrow.core

/**
* Common interface for collections that always have
* at least one element (available from [head]).
*/
public interface NonEmptyCollection<out A> : Collection<A> {
override fun isEmpty(): Boolean = false
public val head: A

public operator fun plus(element: @UnsafeVariance A): NonEmptyCollection<A>
public operator fun plus(elements: Iterable<@UnsafeVariance A>): NonEmptyCollection<A>

public fun toNonEmptySet(): NonEmptySet<A> = toNonEmptySetOrNull()!!
public fun toNonEmptyList(): NonEmptyList<A> = toNonEmptyListOrNull()!!

// These functions take precedence over the extensions in [Collection].
// This way non-emptiness is tracked by the type system.

public fun firstOrNull(): A = head
public fun lastOrNull(): A

public fun distinct(): NonEmptyList<A> =
delegate { it.distinct() }
public fun <K> distinctBy(selector: (A) -> K): NonEmptyList<A> =
delegate { it.distinctBy(selector) }
public fun <B> flatMap(transform: (A) -> NonEmptyCollection<B>): NonEmptyList<B> =
delegate { it.flatMap(transform) }
public fun <B> map(transform: (A) -> B): NonEmptyList<B> =
delegate { it.map(transform) }
public fun <B> mapIndexed(transform: (index:Int, A) -> B): NonEmptyList<B> =
delegate { it.mapIndexed(transform) }
public fun <B> zip(other: NonEmptyCollection<B>): NonEmptyCollection<Pair<A, B>> =
delegate { it.zip(other) }

/**
* Convenience method which delegates the implementation to [Collection],
* and wraps the resulting [List] as a non-empty one.
*/
private inline fun <R> delegate(crossinline f: (Collection<A>) -> List<R>): NonEmptyList<R> =
f(this as Collection<A>).toNonEmptyListOrNull()!!
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ public typealias Nel<A> = NonEmptyList<A>
*
*/
public class NonEmptyList<out A>(
public val head: A,
public override val head: A,
public val tail: List<A>
) : AbstractList<A>() {
) : AbstractList<A>(), NonEmptyCollection<A> {

private constructor(list: List<A>) : this(list[0], list.drop(1))

Expand All @@ -174,20 +174,26 @@ public class NonEmptyList<out A>(

public fun toList(): List<A> = listOf(head) + tail

public inline fun <B> map(f: (A) -> B): NonEmptyList<B> =
NonEmptyList(f(head), tail.map(f))
override fun lastOrNull(): A = when {
tail.isNotEmpty() -> tail.last()
else -> head
}

@Suppress("OVERRIDE_BY_INLINE")
public override inline fun <B> map(transform: (A) -> B): NonEmptyList<B> =
NonEmptyList(transform(head), tail.map(transform))

public inline fun <B> flatMap(f: (A) -> NonEmptyList<B>): NonEmptyList<B> =
f(head) + tail.flatMap { f(it).all }
override fun <B> flatMap(transform: (A) -> NonEmptyCollection<B>): NonEmptyList<B> =
transform(head).toNonEmptyList() + tail.flatMap(transform)

public operator fun plus(l: NonEmptyList<@UnsafeVariance A>): NonEmptyList<A> =
NonEmptyList(all + l.all)
this + l.all

public operator fun plus(l: List<@UnsafeVariance A>): NonEmptyList<A> =
NonEmptyList(all + l)
public override operator fun plus(elements: Iterable<@UnsafeVariance A>): NonEmptyList<A> =
NonEmptyList(all + elements)

public operator fun plus(a: @UnsafeVariance A): NonEmptyList<A> =
NonEmptyList(all + a)
public override operator fun plus(element: @UnsafeVariance A): NonEmptyList<A> =
NonEmptyList(all + element)

public inline fun <B> foldLeft(b: B, f: (B, A) -> B): B =
this.tail.fold(f(b, this.head), f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ import kotlin.jvm.JvmInline
@JvmInline
public value class NonEmptySet<out A> private constructor(
private val elements: Set<A>
) : Set<A> by elements {
) : Set<A> by elements, NonEmptyCollection<A> {

public constructor(first: A, rest: Set<A>) : this(setOf(first) + rest)

public operator fun plus(set: Set<@UnsafeVariance A>): NonEmptySet<A> =
NonEmptySet(elements + set)
public override operator fun plus(elements: Iterable<@UnsafeVariance A>): NonEmptySet<A> =
NonEmptySet(this.elements + elements)

public operator fun plus(element: @UnsafeVariance A): NonEmptySet<A> =
NonEmptySet(elements + element)

public fun <R> map(transform: (@UnsafeVariance A) -> R): NonEmptySet<R> =
NonEmptySet(elements.mapTo(mutableSetOf(), transform))
public override operator fun plus(element: @UnsafeVariance A): NonEmptySet<A> =
NonEmptySet(this.elements + element)

override fun isEmpty(): Boolean = false

override val head: A get() = elements.first()

override fun lastOrNull(): A = elements.last()

override fun toString(): String = "NonEmptySet(${this.joinToString()})"

@Suppress("RESERVED_MEMBER_INSIDE_VALUE_CLASS")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public class ResultRaise(private val raise: Raise<Throwable>) : Raise<Throwable>
@RaiseDSL
@JvmName("bindAllResult")
public fun <A> NonEmptySet<Result<A>>.bindAll(): NonEmptySet<A> =
map { it.bind() }
map { it.bind() }.toNonEmptySet()

@RaiseDSL
public inline fun <A> recover(
Expand Down Expand Up @@ -138,7 +138,7 @@ public class OptionRaise(private val raise: Raise<None>) : Raise<None> by raise
@RaiseDSL
@JvmName("bindAllOption")
public fun <A> NonEmptySet<Option<A>>.bindAll(): NonEmptySet<A> =
map { it.bind() }
map { it.bind() }.toNonEmptySet()

@RaiseDSL
public fun ensure(value: Boolean): Unit = ensure(value) { None }
Expand Down Expand Up @@ -181,7 +181,7 @@ public class IorRaise<Error> @PublishedApi internal constructor(
@RaiseDSL
@JvmName("bindAllIor")
public fun <A> NonEmptySet<Ior<Error, A>>.bindAll(): NonEmptySet<A> =
map { it.bind() }
map { it.bind() }.toNonEmptySet()

@RaiseDSL
public fun <A> Ior<Error, A>.bind(): A =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ public interface Raise<in Error> {

@RaiseDSL
public fun <A> NonEmptySet<Either<Error, A>>.bindAll(): NonEmptySet<A> =
map { it.bind() }
map { it.bind() }.toNonEmptySet()
}

/**
Expand Down