diff --git a/arrow-libs/core/arrow-core-test/src/commonMain/kotlin/arrow/core/test/UnitSpec.kt b/arrow-libs/core/arrow-core-test/src/commonMain/kotlin/arrow/core/test/UnitSpec.kt index 04be368b493..3896f9f4580 100644 --- a/arrow-libs/core/arrow-core-test/src/commonMain/kotlin/arrow/core/test/UnitSpec.kt +++ b/arrow-libs/core/arrow-core-test/src/commonMain/kotlin/arrow/core/test/UnitSpec.kt @@ -1,22 +1,17 @@ package arrow.core.test -import arrow.core.NonEmptyList -import arrow.core.Tuple4 -import arrow.core.Tuple5 +import arrow.core.* import arrow.core.test.generators.unit import arrow.core.test.laws.Law import io.kotest.core.names.TestName -import io.kotest.core.source.SourceRef -import io.kotest.core.spec.RootTest import io.kotest.core.spec.style.StringSpec import io.kotest.core.spec.style.scopes.StringSpecScope import io.kotest.core.spec.style.scopes.addTest -import io.kotest.core.test.TestType import io.kotest.property.Arb import io.kotest.property.Gen import io.kotest.property.PropertyContext import io.kotest.property.arbitrary.bind -import io.kotest.property.arbitrary.filter +import io.kotest.property.arbitrary.filterIsInstance import io.kotest.property.arbitrary.map import io.kotest.property.arbitrary.list as KList import io.kotest.property.arbitrary.map as KMap @@ -38,8 +33,12 @@ public abstract class UnitSpec( public fun Arb.Companion.list(gen: Gen, range: IntRange = 0..maxDepth): Arb> = Arb.KList(gen, range) - public fun Arb.Companion.nonEmptyList(arb: Arb, depth: Int = maxDepth): Arb> = - Arb.list(arb, 1..max(1, depth)).filter(List::isNotEmpty).map(NonEmptyList.Companion::fromListUnsafe) + public fun Arb.Companion.nonEmptyList(arb: Arb, depth: Int = maxDepth): Arb> { + return Arb.list(arb, 1..max(1, depth)) + .map { Option.fromNullable(it.toNonEmptyListOrNull()) } + .filterIsInstance>, Some>>() + .map { it.value } + } public fun Arb.Companion.sequence(arbA: Arb, range: IntRange = 0..maxDepth): Arb> = Arb.list(arbA, range).map { it.asSequence() } diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index b456d2197c7..c97d8c6ca3c 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -751,6 +751,7 @@ public final class arrow/core/NonEmptyListKt { public static final fun sequenceEither (Larrow/core/NonEmptyList;)Larrow/core/Either; public static final fun sequenceOption (Larrow/core/NonEmptyList;)Larrow/core/Option; public static final fun sequenceValidated (Larrow/core/NonEmptyList;Larrow/typeclasses/Semigroup;)Larrow/core/Validated; + public static final fun toNonEmptyListOrNull (Ljava/lang/Iterable;)Larrow/core/NonEmptyList; public static final fun traverse (Larrow/core/NonEmptyList;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;)Larrow/core/Validated; public static final fun traverse (Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function1;)Larrow/core/Either; public static final fun traverse (Larrow/core/NonEmptyList;Lkotlin/jvm/functions/Function1;)Larrow/core/Option; diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index efc7922c75d..123f6b19ac6 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -10,25 +10,27 @@ public typealias Nel = NonEmptyList /** * `NonEmptyList` is a data type used in __Λrrow__ to model ordered lists that guarantee to have at least one value. - * `NonEmptyList` is available in the `arrow-core` module under the `import arrow.core.NonEmptyList` * - * ## nonEmptyListOf + * ## Constructing NonEmptyList * * A `NonEmptyList` guarantees the list always has at least 1 element. * * ```kotlin * import arrow.core.nonEmptyListOf + * import arrow.core.toNonEmptyListOrNull * - * val value = - * //sampleStart - * // nonEmptyListOf() // does not compile - * nonEmptyListOf(1, 2, 3, 4, 5) // NonEmptyList - * //sampleEnd * fun main() { - * println(value) + * println(nonEmptyListOf(1, 2, 3, 4, 5)) + * println(listOf(1, 2, 3).toNonEmptyListOrNull()) + * println(emptyList().toNonEmptyListOrNull()) * } * ``` * + * ```text + * NonEmptyList(1, 2, 3, 4, 5) + * NonEmptyList(1, 2, 3) + * null + * ``` * * ## head * @@ -232,10 +234,26 @@ public class NonEmptyList( public companion object { + @Deprecated( + "Use toNonEmptyListOrNull instead", + ReplaceWith( + "Option.fromNullable>(l.toNonEmptyListOrNull())", + "import arrow.core.toNonEmptyListOrNull", + "import arrow.core.Option", + "import arrow.core.NonEmptyList" + ) + ) @JvmStatic public fun fromList(l: List): Option> = if (l.isEmpty()) None else Some(NonEmptyList(l)) + @Deprecated( + "Use toNonEmptyListOrNull instead", + ReplaceWith( + "l.toNonEmptyListOrNull() ?: throw IndexOutOfBoundsException(\"Empty list doesn't contain element at index 0.\")", + "import arrow.core.toNonEmptyListOrNull" + ) + ) @JvmStatic public fun fromListUnsafe(l: List): NonEmptyList = NonEmptyList(l) @@ -243,26 +261,6 @@ public class NonEmptyList( @PublishedApi internal val unit: NonEmptyList = nonEmptyListOf(Unit) - - @Suppress("UNCHECKED_CAST") - private tailrec fun go( - buf: ArrayList, - f: (A) -> NonEmptyList>, - v: NonEmptyList> - ) { - val head: Either = v.head - when (head) { - is Either.Right -> { - buf += head.value - val x = fromList(v.tail) - when (x) { - is Some>> -> go(buf, f, x.value) - is None -> Unit - } - } - is Either.Left -> go(buf, f, f(head.value) + v.tail) - } - } } public fun zip(fb: NonEmptyList): NonEmptyList> = @@ -418,17 +416,17 @@ public inline fun NonEmptyList.traverseEither(f: (A) -> Either NonEmptyList.traverse(f: (A) -> Either): Either> { - val acc = mutableListOf() - forEach { a -> - when (val res = f(a)) { - is Right -> acc.add(res.value) - is Left -> return@traverse res +public inline fun NonEmptyList.traverse(f: (A) -> Either): Either> = + f(head).map { newHead -> + val acc = mutableListOf() + tail.forEach { a -> + when (val res = f(a)) { + is Right -> acc.add(res.value) + is Left -> return@traverse res + } } + NonEmptyList(newHead, acc) } - // Safe due to traverse laws - return NonEmptyList.fromListUnsafe(acc).right() -} @Deprecated("sequenceEither is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun NonEmptyList>.sequenceEither(): Either> = @@ -453,7 +451,7 @@ public inline fun NonEmptyList.traverse( semigroup: Semigroup, f: (A) -> Validated ): Validated> = - fold(mutableListOf().valid() as Validated>) { acc, a -> + fold>>(mutableListOf().valid()) { acc, a -> when (val res = f(a)) { is Valid -> when (acc) { is Valid -> acc.also { it.value.add(res.value) } @@ -464,7 +462,7 @@ public inline fun NonEmptyList.traverse( is Invalid -> semigroup.run { Invalid(acc.value.combine(res.value)) } } } - }.map { NonEmptyList.fromListUnsafe(it) } + }.map { requireNotNull(it.toNonEmptyListOrNull()) } @Deprecated("sequenceValidated is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun NonEmptyList>.sequenceValidated(semigroup: Semigroup): Validated> = @@ -482,17 +480,17 @@ public inline fun NonEmptyList.traverseOption(f: (A) -> Option): Op @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType -public inline fun NonEmptyList.traverse(f: (A) -> Option): Option> { - val acc = mutableListOf() - forEach { a -> - when (val res = f(a)) { - is Some -> acc.add(res.value) - is None -> return@traverse res +public inline fun NonEmptyList.traverse(f: (A) -> Option): Option> = + f(head).map { newHead -> + val acc = mutableListOf() + tail.forEach { a -> + when (val res = f(a)) { + is Some -> acc.add(res.value) + is None -> return@traverse res + } } + NonEmptyList(newHead, acc) } - // Safe due to traverse laws - return NonEmptyList.fromListUnsafe(acc).some() -} @Deprecated("sequenceOption is being renamed to sequence to simplify the Arrow API", ReplaceWith("sequence()", "arrow.core.sequence")) public fun NonEmptyList>.sequenceOption(): Option> = @@ -500,3 +498,6 @@ public fun NonEmptyList>.sequenceOption(): Option> public fun NonEmptyList>.sequence(): Option> = traverse(::identity) + +public fun Iterable.toNonEmptyListOrNull(): NonEmptyList? = + firstOrNull()?.let { NonEmptyList(it, drop(1)) } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt index 78c4e7dc4e2..a705142bb63 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/IterableTest.kt @@ -197,8 +197,8 @@ class IterableTest : UnitSpec() { ints.map { i -> if (i % 2 == 0) Valid(i) else Invalid(nonEmptyListOf(i)) } .sequence() - val expected: ValidatedNel> = NonEmptyList.fromList(ints.filterNot { it % 2 == 0 }) - .fold({ Valid(ints.filter { it % 2 == 0 }) }, { Invalid(it) }) + val expected: ValidatedNel> = ints.filterNot { it % 2 == 0 } + .toNonEmptyListOrNull()?.invalid() ?: Valid(ints.filter { it % 2 == 0 }) res shouldBe expected } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/MapKTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/MapKTest.kt index 9523814a6a3..478062cae4b 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/MapKTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/MapKTest.kt @@ -98,7 +98,7 @@ class MapKTest : UnitSpec() { ints.traverse(Semigroup.nonEmptyList()) { i -> if (i % 2 == 0) i.validNel() else i.invalidNel() } val expected: ValidatedNel> = - NonEmptyList.fromList(ints.values.filterNot { it % 2 == 0 }) + Option.fromNullable(ints.values.filterNot { it % 2 == 0 }.toNonEmptyListOrNull()) .fold( { ints.entries.filter { (_, v) -> v % 2 == 0 }.map { (k, v) -> k to v }.toMap().validNel() }, { it.invalid() }) diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt index 9722f31bd5c..96d6e0eec39 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptyListTest.kt @@ -18,12 +18,12 @@ class NonEmptyListTest : UnitSpec() { "traverse for Either stack-safe" { // also verifies result order and execution order (l to r) val acc = mutableListOf() - val res = NonEmptyList.fromListUnsafe((0..20_000).toList()).traverse { a -> + val res = (0..20_000).toNonEmptyListOrNull()?.traverse { a -> acc.add(a) Either.Right(a) } - res shouldBe Either.Right(NonEmptyList.fromListUnsafe(acc)) - res shouldBe Either.Right(NonEmptyList.fromListUnsafe((0..20_000).toList())) + res shouldBe Either.Right(acc.toNonEmptyListOrNull()) + res shouldBe Either.Right((0..20_000).toNonEmptyListOrNull()) } "traverse for Either short-circuit" { @@ -53,12 +53,12 @@ class NonEmptyListTest : UnitSpec() { "traverse for Option is stack-safe" { // also verifies result order and execution order (l to r) val acc = mutableListOf() - val res = NonEmptyList.fromListUnsafe((0..20_000).toList()).traverse { a -> + val res = (0..20_000).toNonEmptyListOrNull()?.traverse { a -> acc.add(a) Some(a) } - res shouldBe Some(NonEmptyList.fromListUnsafe(acc)) - res shouldBe Some(NonEmptyList.fromListUnsafe((0..20_000).toList())) + res shouldBe Some(acc.toNonEmptyListOrNull()) + res shouldBe Some((0..20_000).toNonEmptyListOrNull()) } "traverse for Option short-circuits" { @@ -105,8 +105,8 @@ class NonEmptyListTest : UnitSpec() { val res: ValidatedNel> = ints.traverse(Semigroup.nonEmptyList()) { i: Int -> if (i % 2 == 0) i.validNel() else i.invalidNel() } - val expected: ValidatedNel> = NonEmptyList.fromList(ints.filterNot { it % 2 == 0 }) - .fold({ NonEmptyList.fromListUnsafe(ints.filter { it % 2 == 0 }).validNel() }, { it.invalid() }) + val expected: ValidatedNel> = + ints.filterNot { it % 2 == 0 }.toNonEmptyListOrNull()?.invalid() ?: ints.filter { it % 2 == 0 }.toNonEmptyListOrNull()!!.valid() res shouldBe expected } @@ -134,7 +134,7 @@ class NonEmptyListTest : UnitSpec() { "zip2" { checkAll(Arb.nonEmptyList(Arb.int()), Arb.nonEmptyList(Arb.int())) { a, b -> val result = a.zip(b) - val expected = a.all.zip(b.all).let(NonEmptyList.Companion::fromListUnsafe) + val expected = a.all.zip(b.all).toNonEmptyListOrNull() result shouldBe expected } } @@ -146,7 +146,7 @@ class NonEmptyListTest : UnitSpec() { Arb.nonEmptyList(Arb.int()) ) { a, b, c -> val result = a.zip(b, c, ::Triple) - val expected = a.all.zip(b.all, c.all, ::Triple).let(NonEmptyList.Companion::fromListUnsafe) + val expected = a.all.zip(b.all, c.all, ::Triple).toNonEmptyListOrNull() result shouldBe expected } } @@ -159,7 +159,7 @@ class NonEmptyListTest : UnitSpec() { Arb.nonEmptyList(Arb.int()) ) { a, b, c, d -> val result = a.zip(b, c, d, ::Tuple4) - val expected = a.all.zip(b.all, c.all, d.all, ::Tuple4).let(NonEmptyList.Companion::fromListUnsafe) + val expected = a.all.zip(b.all, c.all, d.all, ::Tuple4).toNonEmptyListOrNull() result shouldBe expected } } @@ -173,7 +173,7 @@ class NonEmptyListTest : UnitSpec() { Arb.nonEmptyList(Arb.int()) ) { a, b, c, d, e -> val result = a.zip(b, c, d, e, ::Tuple5) - val expected = a.all.zip(b.all, c.all, d.all, e.all, ::Tuple5).let(NonEmptyList.Companion::fromListUnsafe) + val expected = a.all.zip(b.all, c.all, d.all, e.all, ::Tuple5).toNonEmptyListOrNull() result shouldBe expected } } @@ -189,7 +189,7 @@ class NonEmptyListTest : UnitSpec() { ) { a, b, c, d, e, f -> val result = a.zip(b, c, d, e, f, ::Tuple6) val expected = - a.all.zip(b.all, c.all, d.all, e.all, f.all, ::Tuple6).let(NonEmptyList.Companion::fromListUnsafe) + a.all.zip(b.all, c.all, d.all, e.all, f.all, ::Tuple6).toNonEmptyListOrNull() result shouldBe expected } } @@ -206,7 +206,7 @@ class NonEmptyListTest : UnitSpec() { ) { a, b, c, d, e, f, g -> val result = a.zip(b, c, d, e, f, g, ::Tuple7) val expected = - a.all.zip(b.all, c.all, d.all, e.all, f.all, g.all, ::Tuple7).let(NonEmptyList.Companion::fromListUnsafe) + a.all.zip(b.all, c.all, d.all, e.all, f.all, g.all, ::Tuple7).toNonEmptyListOrNull() result shouldBe expected } } @@ -224,7 +224,7 @@ class NonEmptyListTest : UnitSpec() { ) { a, b, c, d, e, f, g, h -> val result = a.zip(b, c, d, e, f, g, h, ::Tuple8) val expected = a.all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, ::Tuple8) - .let(NonEmptyList.Companion::fromListUnsafe) + .toNonEmptyListOrNull() result shouldBe expected } } @@ -243,7 +243,7 @@ class NonEmptyListTest : UnitSpec() { ) { a, b, c, d, e, f, g, h, i -> val result = a.zip(b, c, d, e, f, g, h, i, ::Tuple9) val expected = a.all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, i.all, ::Tuple9) - .let(NonEmptyList.Companion::fromListUnsafe) + .toNonEmptyListOrNull() result shouldBe expected } } @@ -263,7 +263,7 @@ class NonEmptyListTest : UnitSpec() { ) { a, b, c, d, e, f, g, h, i, j -> val result = a.zip(b, c, d, e, f, g, h, i, j, ::Tuple10) val expected = a.all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, i.all, j.all, ::Tuple10) - .let(NonEmptyList.Companion::fromListUnsafe) + .toNonEmptyListOrNull() result shouldBe expected } } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/SequenceKTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/SequenceKTest.kt index e732c4f43e5..1ad6bffe6f2 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/SequenceKTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/SequenceKTest.kt @@ -65,8 +65,9 @@ class SequenceKTest : UnitSpec() { val res: ValidatedNel> = ints.map { i -> if (i % 2 == 0) i.validNel() else i.invalidNel() } .sequence(Semigroup.nonEmptyList()) - val expected: ValidatedNel> = NonEmptyList.fromList(ints.filterNot { it % 2 == 0 }.toList()) - .fold({ ints.filter { it % 2 == 0 }.validNel() }, { it.invalid() }) + val expected: ValidatedNel> = + ints.filterNot { it % 2 == 0 }.toList() + .toNonEmptyListOrNull()?.invalid() ?: ints.filter { it % 2 == 0 }.validNel() res.map { it.toList() } shouldBe expected.map { it.toList() } } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/java/arrow/core/NonEmptyListUsage.java b/arrow-libs/core/arrow-core/src/jvmTest/java/arrow/core/NonEmptyListUsage.java index ad65e627d07..af0382a41c9 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/java/arrow/core/NonEmptyListUsage.java +++ b/arrow-libs/core/arrow-core/src/jvmTest/java/arrow/core/NonEmptyListUsage.java @@ -18,5 +18,6 @@ public void testUsage() { )); Option> nonEmptyListOption = NonEmptyList.fromList(Arrays.asList(1, 2, 3)); NonEmptyList integers1 = NonEmptyList.fromListUnsafe(Arrays.asList(1, 2, 3)); + NonEmptyList integers2 = NonEmptyListKt.toNonEmptyListOrNull(Arrays.asList(1, 2, 3)); } } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-nonemptylist-01.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-nonemptylist-01.kt index c861853c903..ac3f0223b3c 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-nonemptylist-01.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-nonemptylist-01.kt @@ -2,10 +2,10 @@ package arrow.core.examples.exampleNonemptylist01 import arrow.core.nonEmptyListOf +import arrow.core.toNonEmptyListOrNull -val value = - // nonEmptyListOf() // does not compile - nonEmptyListOf(1, 2, 3, 4, 5) // NonEmptyList fun main() { - println(value) + println(nonEmptyListOf(1, 2, 3, 4, 5)) + println(listOf(1, 2, 3).toNonEmptyListOrNull()) + println(emptyList().toNonEmptyListOrNull()) } diff --git a/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/FilterIndex.kt b/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/FilterIndex.kt index 8774932a02e..d648fc91dd1 100644 --- a/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/FilterIndex.kt +++ b/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/FilterIndex.kt @@ -2,6 +2,7 @@ package arrow.optics.typeclasses import arrow.core.NonEmptyList import arrow.core.Predicate +import arrow.core.toNonEmptyListOrNull import arrow.optics.Every import arrow.optics.Iso import arrow.typeclasses.Monoid @@ -66,16 +67,17 @@ public fun interface FilterIndex { @JvmStatic public fun nonEmptyList(): FilterIndex, Int, A> = FilterIndex { p -> - object : Every, A> { - override fun foldMap(M: Monoid, source: NonEmptyList, map: (A) -> R): R = M.run { - source.foldIndexed(empty()) { index, acc, r -> - if (p(index)) acc.combine(map(r)) else acc - } - } + object : Every, A> { + override fun foldMap(M: Monoid, source: NonEmptyList, map: (A) -> R): R = M.run { + source.foldIndexed(empty()) { index, acc, r -> + if (p(index)) acc.combine(map(r)) else acc + } + } - override fun modify(source: NonEmptyList, map: (focus: A) -> A): NonEmptyList = - NonEmptyList.fromListUnsafe(source.mapIndexed { index, a -> if (p(index)) map(a) else a }) - } + override fun modify(source: NonEmptyList, map: (focus: A) -> A): NonEmptyList = + source.mapIndexed { index, a -> if (p(index)) map(a) else a }.toNonEmptyListOrNull() + ?: throw IndexOutOfBoundsException("Empty list doesn't contain element at index 0.") + } } @JvmStatic diff --git a/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/Index.kt b/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/Index.kt index 8fa3680c464..1bf929f59e6 100644 --- a/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/Index.kt +++ b/arrow-libs/optics/arrow-optics/src/commonMain/kotlin/arrow/optics/typeclasses/Index.kt @@ -3,6 +3,7 @@ package arrow.optics.typeclasses import arrow.core.NonEmptyList import arrow.core.left import arrow.core.right +import arrow.core.toNonEmptyListOrNull import arrow.optics.Fold import arrow.optics.Iso import arrow.optics.Lens @@ -197,9 +198,9 @@ public fun interface Index { POptional( getOrModify = { l -> l.all.getOrNull(i)?.right() ?: l.left() }, set = { l, a -> - NonEmptyList.fromListUnsafe( l.all.mapIndexed { index: Int, aa: A -> if (index == i) a else aa } - ) + .toNonEmptyListOrNull() + ?: throw IndexOutOfBoundsException("Empty list doesn't contain element at index 0.") } ) }