diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt index c52e6e3df05..61aeb1b4e1e 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/EitherTest.kt @@ -9,7 +9,7 @@ import arrow.core.test.laws.MonoidLaws import arrow.core.test.nonEmptyList import arrow.core.test.testLaws import io.kotest.assertions.AssertionErrorBuilder.Companion.fail -import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.property.Arb @@ -241,18 +241,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.string()), Arb.either(Arb.string(), Arb.boolean()), ) { a, b, c, d, e, f, g, h, i -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, d, e, f, g, h, i, ::Tuple9) - val all = listOf(a, b, c, d, e, f, g, h, i) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple9(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g, h, i).simpleFlattenOrAccumulateCombine().map { it.shouldHave9Elements() } + Either.zipOrAccumulate(String::plus, a, b, c, d, e, f, g, h, i, ::Tuple9) shouldBe expected } } @@ -269,21 +259,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.string()), Arb.either(Arb.string(), Arb.boolean()), ) { a, b, c, d, e, f, g, h, i -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, h, i, ::Tuple9) - val all = listOf(a, b, c, d, e, f, g, h, i) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple9(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g, h, i).simpleFlattenOrAccumulate().map { it.shouldHave9Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, g, h, i, ::Tuple9) shouldBe expected } } @@ -300,22 +277,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.string()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.boolean()), ) { a, b, c, d, e, f, g, h, i -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, h, i, ::Tuple9) - val all = listOf(a, b, c, d, e, f, g, h, i) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple9(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g, h, i).simpleFlattenOrAccumulateNel().map { it.shouldHave9Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, g, h, i, ::Tuple9) shouldBe expected } } @@ -325,18 +288,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.short()), Arb.either(Arb.string(), Arb.byte()), ) { a, b -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, ::Pair) - val all = listOf(a, b) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Pair(it[0], it[1]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b).simpleFlattenOrAccumulateCombine().map { it.shouldHave2Elements() } + Either.zipOrAccumulate(String::plus, a, b, ::Pair) shouldBe expected } } @@ -347,18 +300,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.byte()), Arb.either(Arb.string(), Arb.int()), ) { a, b, c -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, ::Triple) - val all = listOf(a, b, c) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Triple(it[0], it[1], it[2]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c).simpleFlattenOrAccumulateCombine().map { it.shouldHave3Elements() } + Either.zipOrAccumulate(String::plus, a, b, c, ::Triple) shouldBe expected } } @@ -370,18 +313,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.int()), Arb.either(Arb.string(), Arb.long()), ) { a, b, c, d -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, d, ::Tuple4) - val all = listOf(a, b, c, d) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple4(it[0], it[1], it[2], it[3]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d).simpleFlattenOrAccumulateCombine().map { it.shouldHave4Elements() } + Either.zipOrAccumulate(String::plus, a, b, c, d, ::Tuple4) shouldBe expected } } @@ -394,18 +327,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.long()), Arb.either(Arb.string(), Arb.float()), ) { a, b, c, d, e -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, d, e, ::Tuple5) - val all = listOf(a, b, c, d, e) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple5(it[0], it[1], it[2], it[3], it[4]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e).simpleFlattenOrAccumulateCombine().map { it.shouldHave5Elements() } + Either.zipOrAccumulate(String::plus, a, b, c, d, e, ::Tuple5) shouldBe expected } } @@ -419,18 +342,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.float()), Arb.either(Arb.string(), Arb.double()), ) { a, b, c, d, e, f -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, d, e, f, ::Tuple6) - val all = listOf(a, b, c, d, e, f) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple6(it[0], it[1], it[2], it[3], it[4], it[5]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f).simpleFlattenOrAccumulateCombine().map { it.shouldHave6Elements() } + Either.zipOrAccumulate(String::plus, a, b, c, d, e, f, ::Tuple6) shouldBe expected } } @@ -445,18 +358,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.double()), Arb.either(Arb.string(), Arb.char()), ) { a, b, c, d, e, f, g -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, d, e, f, g, ::Tuple7) - val all = listOf(a, b, c, d, e, f, g) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple7(it[0], it[1], it[2], it[3], it[4], it[5], it[6]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g).simpleFlattenOrAccumulateCombine().map { it.shouldHave7Elements() } + Either.zipOrAccumulate(String::plus, a, b, c, d, e, f, g, ::Tuple7) shouldBe expected } } @@ -472,18 +375,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.char()), Arb.either(Arb.string(), Arb.string()), ) { a, b, c, d, e, f, g, h -> - val res = Either.zipOrAccumulate({ e1, e2 -> "$e1$e2" }, a, b, c, d, e, f, g, h, ::Tuple8) - val all = listOf(a, b, c, d, e, f, g, h) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().fold("") { acc, t -> "$acc${t.value}" }.left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple8(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g, h).simpleFlattenOrAccumulateCombine().map { it.shouldHave8Elements() } + Either.zipOrAccumulate(String::plus, a, b, c, d, e, f, g, h, ::Tuple8) shouldBe expected } } @@ -493,21 +386,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.short()), Arb.either(Arb.string(), Arb.byte()), ) { a, b -> - val res = Either.zipOrAccumulate(a, b, ::Pair) - val all = listOf(a, b) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Pair(it[0], it[1]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b).simpleFlattenOrAccumulate().map { it.shouldHave2Elements() } + Either.zipOrAccumulate(a, b, ::Pair) shouldBe expected } } @@ -518,21 +398,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.byte()), Arb.either(Arb.string(), Arb.int()), ) { a, b, c -> - val res = Either.zipOrAccumulate(a, b, c, ::Triple) - val all = listOf(a, b, c) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Triple(it[0], it[1], it[2]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c).simpleFlattenOrAccumulate().map { it.shouldHave3Elements() } + Either.zipOrAccumulate(a, b, c, ::Triple) shouldBe expected } } @@ -544,21 +411,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.int()), Arb.either(Arb.string(), Arb.long()), ) { a, b, c, d -> - val res = Either.zipOrAccumulate(a, b, c, d, ::Tuple4) - val all = listOf(a, b, c, d) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple4(it[0], it[1], it[2], it[3]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d).simpleFlattenOrAccumulate().map { it.shouldHave4Elements() } + Either.zipOrAccumulate(a, b, c, d, ::Tuple4) shouldBe expected } } @@ -571,21 +425,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.long()), Arb.either(Arb.string(), Arb.float()), ) { a, b, c, d, e -> - val res = Either.zipOrAccumulate(a, b, c, d, e, ::Tuple5) - val all = listOf(a, b, c, d, e) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple5(it[0], it[1], it[2], it[3], it[4]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e).simpleFlattenOrAccumulate().map { it.shouldHave5Elements() } + Either.zipOrAccumulate(a, b, c, d, e, ::Tuple5) shouldBe expected } } @@ -599,21 +440,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.float()), Arb.either(Arb.string(), Arb.double()), ) { a, b, c, d, e, f -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, ::Tuple6) - val all = listOf(a, b, c, d, e, f) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple6(it[0], it[1], it[2], it[3], it[4], it[5]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f).simpleFlattenOrAccumulate().map { it.shouldHave6Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, ::Tuple6) shouldBe expected } } @@ -628,21 +456,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.double()), Arb.either(Arb.string(), Arb.char()), ) { a, b, c, d, e, f, g -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, ::Tuple7) - val all = listOf(a, b, c, d, e, f, g) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple7(it[0], it[1], it[2], it[3], it[4], it[5], it[6]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g).simpleFlattenOrAccumulate().map { it.shouldHave7Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, g, ::Tuple7) shouldBe expected } } @@ -658,21 +473,8 @@ class EitherTest { Arb.either(Arb.string(), Arb.char()), Arb.either(Arb.string(), Arb.string()), ) { a, b, c, d, e, f, g, h -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, h, ::Tuple8) - val all = listOf(a, b, c, d, e, f, g, h) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>().map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple8(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g, h).simpleFlattenOrAccumulate().map { it.shouldHave8Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, g, h, ::Tuple8) shouldBe expected } } @@ -682,22 +484,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.short()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.byte()), ) { a, b -> - val res = Either.zipOrAccumulate(a, b, ::Pair) - val all = listOf(a, b) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Pair(it[0], it[1]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b).simpleFlattenOrAccumulateNel().map { it.shouldHave2Elements() } + Either.zipOrAccumulate(a, b, ::Pair) shouldBe expected } } @@ -708,22 +496,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.byte()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.int()), ) { a, b, c -> - val res = Either.zipOrAccumulate(a, b, c, ::Triple) - val all = listOf(a, b, c) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Triple(it[0], it[1], it[2]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c).simpleFlattenOrAccumulateNel().map { it.shouldHave3Elements() } + Either.zipOrAccumulate(a, b, c, ::Triple) shouldBe expected } } @@ -735,22 +509,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.int()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.long()), ) { a, b, c, d -> - val res = Either.zipOrAccumulate(a, b, c, d, ::Tuple4) - val all = listOf(a, b, c, d) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple4(it[0], it[1], it[2], it[3]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d).simpleFlattenOrAccumulateNel().map { it.shouldHave4Elements() } + Either.zipOrAccumulate(a, b, c, d, ::Tuple4) shouldBe expected } } @@ -763,22 +523,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.long()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.float()), ) { a, b, c, d, e -> - val res = Either.zipOrAccumulate(a, b, c, d, e, ::Tuple5) - val all = listOf(a, b, c, d, e) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple5(it[0], it[1], it[2], it[3], it[4]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e).simpleFlattenOrAccumulateNel().map { it.shouldHave5Elements() } + Either.zipOrAccumulate(a, b, c, d, e, ::Tuple5) shouldBe expected } } @@ -792,22 +538,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.float()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.double()), ) { a, b, c, d, e, f -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, ::Tuple6) - val all = listOf(a, b, c, d, e, f) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple6(it[0], it[1], it[2], it[3], it[4], it[5]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f).simpleFlattenOrAccumulateNel().map { it.shouldHave6Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, ::Tuple6) shouldBe expected } } @@ -822,22 +554,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.double()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.char()), ) { a, b, c, d, e, f, g -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, ::Tuple7) - val all = listOf(a, b, c, d, e, f, g) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple7(it[0], it[1], it[2], it[3], it[4], it[5], it[6]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g).simpleFlattenOrAccumulateNel().map { it.shouldHave7Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, g, ::Tuple7) shouldBe expected } } @@ -853,22 +571,8 @@ class EitherTest { Arb.either(Arb.nonEmptyList(Arb.int()), Arb.char()), Arb.either(Arb.nonEmptyList(Arb.int()), Arb.string()), ) { a, b, c, d, e, f, g, h -> - val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, h, ::Tuple8) - val all = listOf(a, b, c, d, e, f, g, h) - - val expected = if (all.any { it.isLeft() }) { - all.filterIsInstance>>() - .flatMap { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - all.filterIsInstance>().map { it.value }.let { - Tuple8(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7]).right() - } - } - - res shouldBe expected + val expected = listOf(a, b, c, d, e, f, g, h).simpleFlattenOrAccumulateNel().map { it.shouldHave8Elements() } + Either.zipOrAccumulate(a, b, c, d, e, f, g, h, ::Tuple8) shouldBe expected } } @@ -1016,16 +720,7 @@ class EitherTest { @Test fun toEitherNel() = runTest { checkAll(Arb.either(Arb.string(), Arb.int())) { e -> - e.toEitherNel().let { enel -> - when (e) { - is Left -> { - enel shouldBe listOf(e.value).toNonEmptyListOrNull().left() - } - is Right -> { - enel shouldBe e - } - } - } + e.toEitherNel() shouldBe e.mapLeft { it.nel() } } } @@ -1041,3 +736,43 @@ class EitherTest { } } } + +private fun List<*>.shouldHave2Elements(): Pair<*, *> { + shouldHaveSize(2) + return this[0] to this[1] +} + +private fun List<*>.shouldHave3Elements(): Triple<*, *, *> { + shouldHaveSize(3) + return Triple(this[0], this[1], this[2]) +} + +private fun List<*>.shouldHave4Elements(): Tuple4<*, *, *, *> { + shouldHaveSize(4) + return Tuple4(this[0], this[1], this[2], this[3]) +} + +private fun List<*>.shouldHave5Elements(): Tuple5<*, *, *, *, *> { + shouldHaveSize(5) + return Tuple5(this[0], this[1], this[2], this[3], this[4]) +} + +private fun List<*>.shouldHave6Elements(): Tuple6<*, *, *, *, *, *> { + shouldHaveSize(6) + return Tuple6(this[0], this[1], this[2], this[3], this[4], this[5]) +} + +private fun List<*>.shouldHave7Elements(): Tuple7<*, *, *, *, *, *, *> { + shouldHaveSize(7) + return Tuple7(this[0], this[1], this[2], this[3], this[4], this[5], this[6]) +} + +private fun List<*>.shouldHave8Elements(): Tuple8<*, *, *, *, *, *, *, *> { + shouldHaveSize(8) + return Tuple8(this[0], this[1], this[2], this[3], this[4], this[5], this[6], this[7]) +} + +private fun List<*>.shouldHave9Elements(): Tuple9<*, *, *, *, *, *, *, *, *> { + shouldHaveSize(9) + return Tuple9(this[0], this[1], this[2], this[3], this[4], this[5], this[6], this[7], this[8]) +} 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 2c2357e967d..7fcd91f1f4b 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 @@ -2,9 +2,9 @@ package arrow.core import arrow.core.test.either import arrow.core.test.ior +import arrow.core.test.nonEmptyList import arrow.core.test.option import io.kotest.matchers.nulls.shouldBeNull -import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.boolean @@ -23,36 +23,14 @@ class IterableTest { @Test fun flattenOrAccumulateCombine() = runTest(timeout = 30.seconds) { checkAll(Arb.list(Arb.either(Arb.string(maxSize = 10), Arb.int()), range = 0..20)) { list -> - val expected = - if (list.any { it.isLeft() }) { - list - .filterIsInstance>() - .fold("") { acc, either -> "$acc${either.value}" } - .left() - } else { - list.filterIsInstance>().map { it.value }.right() - } - - list.flattenOrAccumulate(String::plus) shouldBe expected + list.flattenOrAccumulate(String::plus) shouldBe list.simpleFlattenOrAccumulateCombine() } } @Test fun flattenOrAccumulateOk() = runTest { checkAll(Arb.list(Arb.either(Arb.int(), Arb.int()), range = 0..20)) { list -> - val expected = - if (list.any { it.isLeft() }) { - list - .filterIsInstance>() - .map { it.value } - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - list.filterIsInstance>().map { it.value }.right() - } - - list.flattenOrAccumulate() shouldBe expected + list.flattenOrAccumulate() shouldBe list.simpleFlattenOrAccumulate() } } @@ -76,11 +54,7 @@ class IterableTest { checkAll(Arb.list(Arb.int(), range = 0..20)) { ints -> val res = ints.mapOrAccumulate { i -> if (predicate(i)) i else raise(i) } - val expected: Either, List> = - ints.filterNot(::predicate).toNonEmptyListOrNull()?.left() - ?: ints.filter(::predicate).right() - - res shouldBe expected + res shouldBe (ints.filterNot(::predicate).toNonEmptyListOrNull()?.left() ?: ints.right()) } } @@ -475,28 +449,13 @@ class IterableTest { checkAll( Arb.list( Arb.either( - Arb.list(Arb.string(0..5), 0..5), + Arb.nonEmptyList(Arb.string(0..5), 0..5), Arb.int(), ), 0..10, ), ) { list -> - val nelslist = list.mapNotNull { it.withLeftListAsNelOrNull() } - - val expected = - if (nelslist.any { it.isLeft() }) { - nelslist - .filterIsInstance>>() - .map { it.value } - .flatten() - .toNonEmptyListOrNull() - .shouldNotBeNull() - .left() - } else { - nelslist.filterIsInstance>().map { it.value }.right() - } - - nelslist.flattenOrAccumulate() shouldBe expected + list.flattenOrAccumulate() shouldBe list.simpleFlattenOrAccumulateNel() } } @@ -505,29 +464,13 @@ class IterableTest { checkAll( Arb.list( Arb.either( - Arb.list(Arb.string(0..5), 0..5), + Arb.nonEmptyList(Arb.string(0..5), 0..5), Arb.int(), ), 0..10, ), ) { list -> - val nelslist = list.mapNotNull { it.withLeftListAsNelOrNull() } - - val expected = - if (nelslist.any { it.isLeft() }) { - nelslist - .filterIsInstance>>() - .fold("") { accA, either -> - "$accA${ - either.value.fold("") {accB, entry -> "$accB$entry"} - }" - } - .left() - } else { - nelslist.filterIsInstance>().map { it.value }.right() - } - - nelslist.flattenOrAccumulate(String::plus) shouldBe expected + list.flattenOrAccumulate(String::plus) shouldBe list.simpleFlattenOrAccumulateNel().mapLeft { it.reduce(String::plus) } } } @@ -623,10 +566,14 @@ class IterableTest { } } -private fun Either, A>.withLeftListAsNelOrNull(): Either, A>? = when (this) { - is Either.Left -> { - value.toNonEmptyListOrNull()?.left() - } - - is Either.Right -> value.right() +fun List>.simpleFlattenOrAccumulate(): Either, List> { + val (lefts, rights) = this.separateEither() + return lefts.toNonEmptyListOrNull()?.left() ?: rights.right() } + +fun List, B>>.simpleFlattenOrAccumulateNel(): Either, List> = + simpleFlattenOrAccumulate().mapLeft { it.flatten() } + + +fun List>.simpleFlattenOrAccumulateCombine(): Either> = + simpleFlattenOrAccumulate().mapLeft { it.reduce(String::plus) } 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 334ca95f820..01a21e98539 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 @@ -1,19 +1,20 @@ package arrow.core import arrow.core.test.nonEmptyList +import arrow.core.test.nonEmptySet import arrow.platform.stackSafeIteration import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContainOnlyOnce +import io.kotest.matchers.types.shouldBeSameInstanceAs import io.kotest.property.Arb import io.kotest.property.arbitrary.boolean import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.negativeInt import io.kotest.property.arbitrary.pair -import io.kotest.property.arbitrary.set import io.kotest.property.arbitrary.string import io.kotest.property.checkAll import io.kotest.property.exhaustive.exhaustive @@ -87,15 +88,11 @@ class NonEmptyListTest { @Test fun mapOrAccumulateIsStackSafeAndRunsInOriginalOrder() = runTest { val acc = mutableListOf() - val res = (0..stackSafeIteration()) - .toNonEmptyListOrNull() - .shouldNotBeNull() - .mapOrAccumulate(String::plus) { - acc.add(it) - it - } - res shouldBe Either.Right(acc) - res shouldBe Either.Right((0..stackSafeIteration()).toList()) + NonEmptyList(0, (1..stackSafeIteration()).toList()).mapOrAccumulate(String::plus) { + acc.add(it) + it + }.merge() shouldBe acc + acc shouldBe (0..stackSafeIteration()).toList() } @Test @@ -105,8 +102,7 @@ class NonEmptyListTest { if (i % 2 == 0) i else raise(i) } - val expected = nel.filterNot { it % 2 == 0 } - .toNonEmptyListOrNull()?.left() ?: nel.filter { it % 2 == 0 }.right() + val expected = nel.filterNot { it % 2 == 0 }.toNonEmptyListOrNull()?.left() ?: nel.right() res shouldBe expected } @@ -177,7 +173,7 @@ class NonEmptyListTest { fun zip2() = runTest { checkAll(Arb.nonEmptyList(Arb.int()), Arb.nonEmptyList(Arb.int())) { a, b -> val result = a.zip(b) - val expected = a.all.zip(b.all).toNonEmptyListOrNull() + val expected = a.all.zip(b.all) result shouldBe expected } } @@ -190,7 +186,7 @@ class NonEmptyListTest { Arb.nonEmptyList(Arb.int()), ) { a, b, c -> val result = a.zip(b, c, ::Triple) - val expected = a.all.zip(b.all, c.all, ::Triple).toNonEmptyListOrNull() + val expected = a.all.zip(b.all, c.all, ::Triple) result shouldBe expected } } @@ -204,7 +200,7 @@ class NonEmptyListTest { 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).toNonEmptyListOrNull() + val expected = a.all.zip(b.all, c.all, d.all, ::Tuple4) result shouldBe expected } } @@ -219,7 +215,7 @@ class NonEmptyListTest { 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).toNonEmptyListOrNull() + val expected = a.all.zip(b.all, c.all, d.all, e.all, ::Tuple5) result shouldBe expected } } @@ -235,8 +231,7 @@ class NonEmptyListTest { Arb.nonEmptyList(Arb.int()), ) { 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).toNonEmptyListOrNull() + val expected = a.all.zip(b.all, c.all, d.all, e.all, f.all, ::Tuple6) result shouldBe expected } } @@ -253,8 +248,7 @@ class NonEmptyListTest { Arb.nonEmptyList(Arb.int()), ) { 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).toNonEmptyListOrNull() + val expected = a.all.zip(b.all, c.all, d.all, e.all, f.all, g.all, ::Tuple7) result shouldBe expected } } @@ -273,7 +267,6 @@ class NonEmptyListTest { ) { 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) - .toNonEmptyListOrNull() result shouldBe expected } } @@ -293,7 +286,6 @@ class NonEmptyListTest { ) { 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) - .toNonEmptyListOrNull() result shouldBe expected } } @@ -422,22 +414,16 @@ class NonEmptyListTest { @Test fun toList() = runTest { - checkAll(Arb.list(Arb.int(), range = 0..20)) { a -> - a.toNonEmptyListOrNull() - ?.toList() - ?.shouldBe(a) + checkAll(Arb.nonEmptyList(Arb.int(), range = 1..20)) { a -> + a.toList() shouldBe a } } @Test fun distinct() = runTest { val ex = listOf(1, 2, 3, 4, 5).exhaustive() - checkAll(Arb.list(ex, range = 0..20)) { a -> - val expected = a.distinct() - - a.toNonEmptyListOrNull() - ?.distinct() - ?.shouldBe(expected) + checkAll(Arb.nonEmptyList(ex, range = 0..20)) { a -> + a.distinct() shouldBe a.all.distinct() } } @@ -446,12 +432,8 @@ class NonEmptyListTest { val ex = listOf(1, 2, 3, 4, 5).exhaustive() fun selector(i: Int) = i % 2 == 0 - checkAll(Arb.list(ex, range = 0..20)) { a -> - val expected = a.distinctBy(::selector) - - a.toNonEmptyListOrNull() - ?.distinctBy(::selector) - ?.shouldBe(expected) + checkAll(Arb.nonEmptyList(ex, range = 0..20)) { a -> + a.distinctBy(::selector) shouldBe a.all.distinctBy(::selector) } } @@ -473,23 +455,19 @@ class NonEmptyListTest { @Test fun plusIterable() = runTest { checkAll( - Arb.list(Arb.int(), range = 0..10), + Arb.nonEmptyList(Arb.int(), range = 1..10), Arb.list(Arb.int(), range = 0..10), ) { a, b -> - a.toNonEmptyListOrNull() - ?.also { - it + b shouldBe (a + b) - } + a + b shouldBe (a.all + b) } } @Test fun toStringContainsNelValues() = runTest { - checkAll(20, Arb.set(Arb.int(0..9), 0..10)) { a -> - a.toNonEmptyListOrNull().toString().let { s -> - a.forEach { - s shouldContainOnlyOnce it.toString() - } + checkAll(20, Arb.nonEmptySet(Arb.int(0..9), 0..10)) { a -> + val s = a.toString() + a.toNonEmptyList().forEach { + s shouldContainOnlyOnce it.toString() } } } @@ -523,60 +501,42 @@ class NonEmptyListTest { ) { 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) - .toNonEmptyListOrNull() result shouldBe expected } } @Test fun compareTo() = runTest { - checkAll(Arb.list(Arb.int(), 1..10), Arb.list(Arb.int(), 1..10)) { a, b -> - val expected = a.compareTo(b) - - a.toNonEmptyListOrThrow() - .compareTo(b.toNonEmptyListOrThrow()) shouldBe expected + checkAll(Arb.nonEmptyList(Arb.int(), 1..10), Arb.nonEmptyList(Arb.int(), 1..10)) { a, b -> + a.compareTo(b) shouldBe a.all.compareTo(b.all) } } @Test fun flatten() = runTest { - checkAll(Arb.list(Arb.list(Arb.int(), 1..10), 1..10)) { a -> - val expected = a.flatten() - - a.map { it.toNonEmptyListOrThrow() } - .toNonEmptyListOrThrow().flatten() shouldBe expected + checkAll(Arb.nonEmptyList(Arb.nonEmptyList(Arb.int(), 1..10), 1..10)) { a -> + a.flatten() shouldBe a.map { it.all }.all.flatten() } } @Test fun unzip() = runTest { - checkAll(Arb.list(Arb.pair(Arb.int(), Arb.string(0..10)), 1..10)) { a -> - val expA = a.map { it.first } - val expB = a.map { it.second } - - with(a.toNonEmptyListOrThrow().unzip()) { - first shouldBe expA - second shouldBe expB - } + checkAll(Arb.nonEmptyList(Arb.pair(Arb.int(), Arb.string(0..10)), 1..10)) { a -> + a.unzip() shouldBe (a.map { it.first } to a.map { it.second }) } } + @OptIn(PotentiallyUnsafeNonEmptyOperation::class) + @Test + fun wrapAsNonEmptyListOrThrowEmpty() = runTest { + runCatching { emptyList().wrapAsNonEmptyListOrThrow() }.isFailure.shouldBeTrue() + } + @OptIn(PotentiallyUnsafeNonEmptyOperation::class) @Test fun wrapAsNonEmptyListOrThrow() = runTest { - checkAll(Arb.list(Arb.int(), 0..10)) { a -> - runCatching { - a.wrapAsNonEmptyListOrThrow() - }.also { - when (a.isEmpty()) { - true -> { - it.isFailure shouldBe true - } - false -> { - it.getOrNull() shouldBe a.toNonEmptyListOrThrow() - } - } - } + checkAll(Arb.list(Arb.int(), 1..10)) { a -> + a.wrapAsNonEmptyListOrThrow().all shouldBeSameInstanceAs a } } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt index 32684808dda..5a5b98babf1 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/NonEmptySetTest.kt @@ -8,11 +8,11 @@ import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContainOnlyOnce +import io.kotest.matchers.types.shouldBeSameInstanceAs import io.kotest.property.Arb import io.kotest.property.arbitrary.Codepoint import io.kotest.property.arbitrary.az import io.kotest.property.arbitrary.int -import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.negativeInt import io.kotest.property.arbitrary.next import io.kotest.property.arbitrary.orNull @@ -75,7 +75,7 @@ class NonEmptySetTest { checkAll( Arb.nonEmptySet(Arb.int()), ) { nes -> - val s = nes.toSet().toNonEmptySetOrThrow() + val s = nes.toNonEmptyList().toNonEmptySet() (nes == s).shouldBeTrue() // `shouldBe` doesn't use the `equals` methods on `Iterable` nes.hashCode() shouldBe s.hashCode() } @@ -84,13 +84,11 @@ class NonEmptySetTest { @Test fun mapOrAccumulateIsStackSafe() = runTest { val acc = mutableSetOf() - val res = (0..stackSafeIteration()) - .toNonEmptySetOrThrow() - .mapOrAccumulate(String::plus) { - acc.add(it) - it - } - res shouldBe Either.Right(acc) + NonEmptySet(0, 1..stackSafeIteration()).mapOrAccumulate(String::plus) { + acc.add(it) + it + }.merge() shouldBe acc + acc shouldBe (0..stackSafeIteration()).toSet() } @Test @@ -100,10 +98,7 @@ class NonEmptySetTest { if (i % 2 == 0) i else raise(i) } - val expected = nes.filterNot { it % 2 == 0 } - .toNonEmptyListOrNull()?.left() ?: nes.filter { it % 2 == 0 }.toNonEmptySetOrThrow().right() - - res shouldBe expected + res shouldBe (nes.filterNot { it % 2 == 0 }.toNonEmptyListOrNull()?.left() ?: nes.right()) } } @@ -120,104 +115,91 @@ class NonEmptySetTest { @Test fun head() = runTest { - checkAll(Arb.set(Arb.int(), range = 1..10)) { a -> - a.toNonEmptySetOrThrow().head shouldBe a.first() + checkAll(Arb.nonEmptySet(Arb.int(), range = 1..10)) { a -> + a.head shouldBe a.elements.first() } } @Test fun lastOrNull() = runTest { - checkAll(Arb.set(Arb.int(), range = 1..10)) { a -> - a.toNonEmptySetOrThrow().lastOrNull() shouldBe a.last() + checkAll(Arb.nonEmptySet(Arb.int(), range = 1..10)) { a -> + a.lastOrNull() shouldBe a.elements.last() } } @Test fun toStringContainsData() = runTest { - checkAll(Arb.set(Arb.int(0..9), 1..9)) { a -> - a.toNonEmptySetOrThrow().toString().also { s -> - a.onEach { i -> - s shouldContainOnlyOnce i.toString() - } + checkAll(Arb.nonEmptySet(Arb.int(0..9), 1..9)) { a -> + val s = a.toString() + a.forEach { i -> + s shouldContainOnlyOnce i.toString() } } } @Test fun distinct() = runTest { - checkAll(Arb.list(Arb.int(0..5), 1..30)) { a -> - val expected = a.distinct() - - a.toNonEmptySetOrThrow().also { nes -> - nes.distinct() shouldBe expected - nes.toList() shouldBe expected // the same as distinct - } + checkAll(Arb.nonEmptySet(Arb.int(0..5), 1..5)) { a -> + val expected = a.elements.distinct() + a.distinct() shouldBe expected + a.toList() shouldBe expected // the same as distinct } } @Test fun distinctBy() = runTest { - checkAll(30, Arb.list(Arb.string(1, 3, Codepoint.az()), 1..50)) { a -> + checkAll(30, Arb.nonEmptySet(Arb.string(1, 3, Codepoint.az()), 1..50)) { a -> fun selector(s: String) = s[0] - a.toNonEmptySetOrThrow() - .distinctBy(::selector) shouldBe a.distinctBy(::selector) + a.distinctBy(::selector) shouldBe a.elements.distinctBy(::selector) } } @Test fun flatMap() = runTest { - checkAll(30, Arb.set(Arb.int(), 1..10)) { a -> - fun transform(i: Int) = listOf(i, i + 1, i * 2) + checkAll(30, Arb.nonEmptySet(Arb.int(), 1..10)) { a -> + fun transform(i: Int) = nonEmptyListOf(i, i + 1, i * 2) .map { it.toString() } - .toNonEmptyListOrThrow() - a.toNonEmptySetOrThrow() - .flatMap(::transform) shouldBe a.flatMap(::transform) + a.flatMap(::transform) shouldBe a.elements.flatMap(::transform) } } @Test fun mapIndexed() = runTest { - checkAll(Arb.set(Arb.int(), 1..30)) { a -> + checkAll(Arb.nonEmptySet(Arb.int(), 1..30)) { a -> fun transform(index: Int, i: Int) = when (index) { in 0..10 -> i * 2 in 11..20 -> i * 3 else -> i * 4 }.toString() - a.toNonEmptySetOrThrow() - .mapIndexed(::transform) shouldBe a.mapIndexed(::transform) + a.mapIndexed(::transform) shouldBe a.elements.mapIndexed(::transform) } } @Test fun zip() = runTest { - checkAll(Arb.set(Arb.int(), 1..30), Arb.nonEmptyList(Arb.string(0..5), 1..30)) { a, b -> + checkAll(Arb.nonEmptySet(Arb.int(), 1..30), Arb.nonEmptyList(Arb.string(0..5), 1..30)) { a, b -> val expected = (0 until min(a.size, b.size)).map { a.elementAt(it) to b[it] } - a.toNonEmptySetOrThrow().zip(b) shouldBe expected + a.zip(b) shouldBe expected } } @OptIn(PotentiallyUnsafeNonEmptyOperation::class) @Test - fun wrapAsNonEmptySetOrThrow() = runTest { - checkAll(Arb.set(Arb.int(), 0..10)) { a -> - runCatching { - a.wrapAsNonEmptySetOrThrow() - }.also { - when (a.isEmpty()) { - true -> { - it.isFailure shouldBe true - } - false -> { - it.getOrNull() shouldBe a.toNonEmptySetOrThrow() - } - } - } + fun wrapAsNonEmptySetOrThrowEmpty() = runTest { + runCatching { emptySet().wrapAsNonEmptySetOrThrow() }.isFailure.shouldBeTrue() + } + + @OptIn(PotentiallyUnsafeNonEmptyOperation::class) + @Test + fun wrapAsNonEmptySetOrThrowNonEmpty() = runTest { + checkAll(Arb.set(Arb.int(), 1..10)) { a -> + a.wrapAsNonEmptySetOrThrow().elements shouldBeSameInstanceAs a } } 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 529a53237f0..e04dc36609b 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 @@ -483,21 +483,16 @@ class SequenceKTest { val errors = mutableListOf() val successes = a.mapNotNull { s -> - runCatching { s.toInt() } - .onFailure { errors.add(s) } - .getOrNull() - } - - val expected = if (errors.isNotEmpty()) { - errors.toNonEmptyListOrThrow().left() - } else { - successes.right() + s.toIntOrNull() ?: run { + errors.add(s) + null + } } a.asSequence().mapOrAccumulate { ensure(it[0].isDigit()) { it } it.toInt() - } shouldBe expected + } shouldBe (errors.toNonEmptyListOrNull()?.left() ?: successes.right()) } } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EffectSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EffectSpec.kt index 2e54a710c66..7334745be0e 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EffectSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/EffectSpec.kt @@ -603,10 +603,8 @@ class EffectSpec { } @Test fun accumulateReturnsEveryError() = runTest { - checkAll(Arb.list(Arb.int(), range = 2..20)) { errors -> - either, List> { - mapOrAccumulate(errors) { raise(it) } - } shouldBe errors.toNonEmptyListOrNull()!!.left() + checkAll(Arb.nonEmptyList(Arb.int(), range = 2..20)) { errors -> + either { mapOrAccumulate(errors) { raise(it) } } shouldBe errors.left() } } @@ -620,9 +618,7 @@ class EffectSpec { @Test fun nonEmptyListMapOrAccumulateReturnsEveryError() = runTest { checkAll(Arb.nonEmptyList(Arb.int(), range = 2..20)) { errors -> - either, NonEmptyList> { - mapOrAccumulate(errors) { raise(it) } - } shouldBe errors.toNonEmptyListOrNull()!!.left() + either { mapOrAccumulate(errors) { raise(it) } } shouldBe errors.left() } } @@ -636,9 +632,7 @@ class EffectSpec { @Test fun nonEmptySetMapOrAccumulateReturnsEveryError() = runTest { checkAll(Arb.nonEmptySet(Arb.int(), range = 2..20)) { errors -> - either, NonEmptySet> { - mapOrAccumulate(errors) { raise(it) } - } shouldBe errors.toNonEmptyListOrNull()!!.left() + either { mapOrAccumulate(errors) { raise(it) } } shouldBe errors.toNonEmptyList().left() } } @@ -664,10 +658,10 @@ class EffectSpec { val expected = eithers.mapNotNull { it.leftOrNull() }.toNonEmptyListOrNull()?.left() ?: eithers.mapNotNull { it.getOrNull() }.right() - either, List> { + either { zipOrAccumulate( { eithers.bindAll() }, - { emptyList() } + { emptyList() } ) { a, b -> a + b } } shouldBe expected } @@ -687,10 +681,10 @@ class EffectSpec { val expected = eithers.mapNotNull { it.leftOrNull() }.toNonEmptyListOrNull()?.left() ?: eithers.mapNotNull { it.getOrNull() }.right() - either, NonEmptyList> { + either { zipOrAccumulate( { eithers.bindAll() }, - { emptyList() } + { emptyList() } ) { a, b -> a + b } } shouldBe expected } @@ -710,10 +704,10 @@ class EffectSpec { val expected = eithers.mapNotNull { it.leftOrNull() }.toNonEmptyListOrNull()?.left() ?: eithers.mapNotNull { it.getOrNull() }.toSet().right() - either, NonEmptySet> { + either { zipOrAccumulate( { eithers.bindAll() }, - { emptySet() } + { emptySet() } ) { a, b -> a + b } } shouldBe expected } diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt index 00955923419..e17aaad2d7f 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/IorSpec.kt @@ -11,15 +11,13 @@ import arrow.core.rightIor import arrow.core.shouldThrow import arrow.core.test.ior import arrow.core.test.nonEmptyList +import arrow.core.test.nonEmptySet import arrow.core.toIorNel -import arrow.core.toNonEmptyListOrThrow -import arrow.core.toNonEmptySetOrThrow import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.map -import io.kotest.property.arbitrary.set import io.kotest.property.arbitrary.string import io.kotest.property.checkAll import kotlinx.coroutines.async @@ -211,8 +209,7 @@ class IorSpec { @Test fun bindAllNel() = runTest { - checkAll(iterations, Arb.list(iorArb, nelRange)) { generated -> - val a = generated.toNonEmptyListOrThrow() + checkAll(iterations, Arb.nonEmptyList(iorArb, nelRange)) { a -> val expected = a.fold(listOf().rightIor(), iorFold(String::plus)) @@ -224,11 +221,10 @@ class IorSpec { @Test fun bindAllNes() = runTest { - checkAll(iterations, Arb.set(iorArb, nelRange)) { generated -> - val a = generated.toNonEmptySetOrThrow() + checkAll(iterations, Arb.nonEmptySet(iorArb, nelRange)) { a -> val expected = a.fold(listOf().rightIor(), iorFold(String::plus)) - .map { l -> l.toNonEmptySetOrThrow() } + .map { l -> l.toSet() } ior(String::plus) { a.bindAll() diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt index c824530e4e6..015d8c4dda5 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/ResultSpec.kt @@ -1,14 +1,13 @@ package arrow.core.raise +import arrow.core.test.nonEmptyList +import arrow.core.test.nonEmptySet import arrow.core.test.result -import arrow.core.toNonEmptyListOrThrow -import arrow.core.toNonEmptySetOrThrow import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.map -import io.kotest.property.arbitrary.set import io.kotest.property.checkAll import kotlinx.coroutines.test.runTest import kotlin.test.Test @@ -86,8 +85,7 @@ class ResultSpec { @Test fun bindAllNel() = runTest { - checkAll(iterations, Arb.list(Arb.result(Arb.int()), nelRange)) { generated -> - val a = generated.toNonEmptyListOrThrow() + checkAll(iterations, Arb.nonEmptyList(Arb.result(Arb.int()), nelRange)) { a -> val expected = a.firstOrNull { it.isFailure } ?: Result.success( a.map { @@ -103,14 +101,9 @@ class ResultSpec { @Test fun bindAllNes() = runTest { - checkAll(iterations, Arb.set(Arb.result(Arb.int()), nelRange)) { generated -> - val a = generated.toNonEmptySetOrThrow() + checkAll(iterations, Arb.nonEmptySet(Arb.result(Arb.int()), nelRange)) { a -> val expected = a.firstOrNull { it.isFailure } - ?: Result.success( - a.map { - it.getOrThrow() - }.toNonEmptySetOrThrow(), - ) + ?: Result.success(a.map { it.getOrThrow() }.toNonEmptySet()) result { a.bindAll() diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/SingletonSpec.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/SingletonSpec.kt index bbbda3ad6ba..88e2061c8b8 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/SingletonSpec.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/raise/SingletonSpec.kt @@ -1,16 +1,13 @@ package arrow.core.raise -import arrow.core.NonEmptyList -import arrow.core.NonEmptySet import arrow.core.None +import arrow.core.Option +import arrow.core.Some import arrow.core.getOrElse -import arrow.core.mapValuesNotNull import arrow.core.shouldBeTypeOf +import arrow.core.test.nonEmptyList +import arrow.core.test.nonEmptySet import arrow.core.test.option -import arrow.core.toNonEmptyListOrNull -import arrow.core.toNonEmptyListOrThrow -import arrow.core.toNonEmptySetOrNull -import arrow.core.toNonEmptySetOrThrow import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.boolean @@ -18,10 +15,11 @@ import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.map import io.kotest.property.arbitrary.orNull -import io.kotest.property.arbitrary.set import io.kotest.property.arbitrary.string import io.kotest.property.checkAll import kotlinx.coroutines.test.runTest +import kotlin.collections.component1 +import kotlin.collections.component2 import kotlin.test.Test class SingletonSpec { @@ -129,179 +127,76 @@ class SingletonSpec { @Test fun mapNullableBindAll() = runTest { checkAll(iterations, Arb.map(Arb.int(), Arb.int().orNull(), range.first, range.last)) { a -> - val recovered: Map = emptyMap() - val expected = if (a.containsValue(null)) { - recovered - } else { - a.mapValuesNotNull { it.value } - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + val recovered = emptyMap() + singleton({ recovered }) { a.bindAll() } shouldBe if (a.containsValue(null)) recovered else a } } @Test fun mapOptionBindAll() = runTest { - checkAll(iterations, Arb.map(Arb.int(), Arb.option(Arb.int()), range.first, range.last)) { a -> - val recovered: Map = emptyMap() - val expected = if (a.containsValue(None)) { - recovered - } else { - a.mapValuesNotNull { it.value.getOrNull() } - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + checkAll(iterations, Arb.map(Arb.int(), Arb.option(Arb.int().orNull()), range.first, range.last)) { a -> + val recovered = emptyMap() + singleton({ recovered }) { a.bindAll() } shouldBe if (a.containsValue(None)) recovered else a.mapValues { (_, v) -> v.shouldBeSome() } } } @Test fun iterableNullableBindAll() = runTest { checkAll(iterations, Arb.list(Arb.int().orNull(), range)) { a -> - val recovered: List = emptyList() - val expected = if (a.contains(null)) { - recovered - } else { - a.mapNotNull { it } - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + val recovered = emptyList() + singleton({ recovered }) { a.bindAll() } shouldBe if (a.contains(null)) recovered else a } } @Test fun iterableOptionBindAll() = runTest { - checkAll(iterations, Arb.list(Arb.option(Arb.int()), range)) { a -> - val recovered: List = emptyList() - val expected = if (a.contains(None)) { - recovered - } else { - a.mapNotNull { it.getOrNull() } - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + checkAll(iterations, Arb.list(Arb.option(Arb.int().orNull()), range)) { a -> + val recovered = emptyList() + singleton({ recovered }) { a.bindAll() } shouldBe if (a.contains(None)) recovered else a.map { it.shouldBeSome() } } } @Test fun nelNullableBindAll() = runTest { - checkAll(iterations, Arb.list(Arb.int().orNull(), nelRange)) { generated -> - val a = generated.toNonEmptyListOrThrow() - val recovered: NonEmptyList? = null - - val expected = if (a.contains(null)) { - recovered - } else { - a.mapNotNull { it }.toNonEmptyListOrNull() - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + checkAll(iterations, Arb.nonEmptyList(Arb.int().orNull(), nelRange)) { a -> + nullable { a.bindAll() } shouldBe if (a.contains(null)) null else a } } @Test fun nelOptionBindAll() = runTest { - checkAll(iterations, Arb.list(Arb.option(Arb.int()), nelRange)) { generated -> - val a = generated.toNonEmptyListOrThrow() - val recovered: NonEmptyList? = null - - val expected = if (a.contains(None)) { - recovered - } else { - a.mapNotNull { it.getOrNull() }.toNonEmptyListOrNull() - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + checkAll(iterations, Arb.nonEmptyList(Arb.option(Arb.int().orNull()), nelRange)) { a -> + nullable { a.bindAll() } shouldBe if (a.contains(None)) null else a.map { it.shouldBeSome() } } } @Test fun nesNullableBindAll() = runTest { - checkAll(iterations, Arb.set(Arb.int().orNull(), nelRange)) { generated -> - val a = generated.toNonEmptySetOrThrow() - val recovered: NonEmptySet? = null - - val expected = if (a.contains(null)) { - recovered - } else { - a.mapNotNull { it }.toNonEmptySetOrNull() - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + checkAll(iterations, Arb.nonEmptySet(Arb.int().orNull(), nelRange)) { a -> + nullable { a.bindAll() } shouldBe if (a.contains(null)) null else a } } @Test fun nesOptionBindAll() = runTest { - checkAll(iterations, Arb.set(Arb.option(Arb.int()), nelRange)) { generated -> - val a = generated.toNonEmptySetOrThrow() - val recovered: NonEmptySet? = null - - val expected = if (a.contains(None)) { - recovered - } else { - a.mapNotNull { it.getOrNull() }.toNonEmptySetOrNull() - } - - singleton( - { recovered }, - { a.bindAll() }, - ) shouldBe expected + checkAll(iterations, Arb.nonEmptySet(Arb.option(Arb.int().orNull()), nelRange)) { a -> + nullable { a.bindAll() } shouldBe if (a.contains(None)) null else a.map { it.shouldBeSome() }.toNonEmptySet() } } @Test fun recover() = runTest { checkAll(iterations, Arb.int().orNull()) { a -> - val expected = a?.toString() ?: "recovered" - - singleton( - { "" }, - { - recover( - { - a.bind().toString() - }, - { "recovered" }, - ) - }, - ) shouldBe expected + singleton({ "" }) { recover({ a.bind().toString() }, { "recovered" }) } shouldBe (a?.toString() ?: "recovered") } } @Test fun ignoreErrors() = runTest { checkAll(iterations, Arb.int().orNull()) { a -> - val expected = a ?: 0 - - singleton( - { 0 }, - { - ignoreErrors { - a.bind() - } - }, - ) shouldBe expected + singleton({ 0 }) { ignoreErrors { a.bind() } } shouldBe (a ?: 0) } } } + +private fun Option.shouldBeSome(): A = shouldBeTypeOf>().value diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/test/Generators.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/test/Generators.kt index 3885baf1f0f..026fbb4866e 100644 --- a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/test/Generators.kt +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/test/Generators.kt @@ -2,6 +2,7 @@ package arrow.core.test import arrow.core.* import io.kotest.property.Arb +import io.kotest.property.Gen import io.kotest.property.arbitrary.arbitrary import io.kotest.property.arbitrary.bind import io.kotest.property.arbitrary.boolean @@ -29,13 +30,14 @@ import kotlin.coroutines.startCoroutine // copied from kotest-extensions-arrow -fun Arb.Companion.nonEmptyList(arb: Arb, range: IntRange = 0 .. 100): Arb> = +fun Arb.Companion.nonEmptyList(arb: Gen, range: IntRange = 0 .. 100): Arb> = Arb.list(arb, max(range.first, 1) .. range.last) .filter { it.isNotEmpty() } .map { NonEmptyList(it) } -fun Arb.Companion.nonEmptySet(arb: Arb, range: IntRange = 0 .. 100): Arb> = - Arb.set(arb, max(range.first, 1) .. range.last).map { it.toNonEmptySetOrNull()!! } +@OptIn(PotentiallyUnsafeNonEmptyOperation::class) +fun Arb.Companion.nonEmptySet(arb: Gen, range: IntRange = 0 .. 100): Arb> = + Arb.set(arb, max(range.first, 1) .. range.last).map { it.wrapAsNonEmptySetOrThrow() } fun Arb.Companion.sequence(arb: Arb, range: IntRange = 0 .. 100): Arb> = Arb.list(arb, range).map { it.asSequence() } diff --git a/arrow-libs/integrations/arrow-core-jackson/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt b/arrow-libs/integrations/arrow-core-jackson/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt index 0278a2dffb4..7643cbcb22d 100644 --- a/arrow-libs/integrations/arrow-core-jackson/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt +++ b/arrow-libs/integrations/arrow-core-jackson/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt @@ -5,17 +5,16 @@ import arrow.core.Ior import arrow.core.NonEmptyList import arrow.core.NonEmptySet import arrow.core.Option +import arrow.core.PotentiallyUnsafeNonEmptyOperation import arrow.core.bothIor import arrow.core.leftIor import arrow.core.rightIor -import arrow.core.toNonEmptyListOrNull -import arrow.core.toNonEmptySetOrNull import arrow.core.toOption +import arrow.core.wrapAsNonEmptyListOrThrow import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.arbitrary import io.kotest.property.arbitrary.choice -import io.kotest.property.arbitrary.filter import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.map import io.kotest.property.arbitrary.orNull @@ -45,9 +44,10 @@ fun Arb.Companion.option(arb: Arb): Arb> = arb.orNull().map { i fun Arb.Companion.either(left: Arb, right: Arb): Arb> = choice(left.map { Either.Left(it) }, right.map { Either.Right(it) }) -fun Arb.Companion.nonEmptyList(a: Arb): Arb> = list(a).filter(List::isNotEmpty).map { it.toNonEmptyListOrNull()!! } +@OptIn(PotentiallyUnsafeNonEmptyOperation::class) +fun Arb.Companion.nonEmptyList(a: Arb): Arb> = list(a, 1..100).map { it.wrapAsNonEmptyListOrThrow() } -fun Arb.Companion.nonEmptySet(a: Arb): Arb> = list(a).filter(List::isNotEmpty).map { it.toNonEmptySetOrNull()!! } +fun Arb.Companion.nonEmptySet(a: Arb): Arb> = nonEmptyList(a).map { it.toNonEmptySet() } fun Arb.Companion.ior(arbL: Arb, arbR: Arb): Arb> = Arb.choice( arbitrary { arbL.bind().leftIor() }, diff --git a/arrow-libs/integrations/arrow-core-jackson2/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt b/arrow-libs/integrations/arrow-core-jackson2/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt index 58fabdba495..4a65c33fe0f 100644 --- a/arrow-libs/integrations/arrow-core-jackson2/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt +++ b/arrow-libs/integrations/arrow-core-jackson2/src/test/kotlin/arrow/integrations/jackson/module/TestHelper.kt @@ -5,19 +5,18 @@ import arrow.core.Ior import arrow.core.NonEmptyList import arrow.core.NonEmptySet import arrow.core.Option +import arrow.core.PotentiallyUnsafeNonEmptyOperation import arrow.core.bothIor import arrow.core.leftIor import arrow.core.rightIor -import arrow.core.toNonEmptyListOrNull -import arrow.core.toNonEmptySetOrNull import arrow.core.toOption +import arrow.core.wrapAsNonEmptyListOrThrow import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonTypeRef import io.kotest.matchers.shouldBe import io.kotest.property.Arb import io.kotest.property.arbitrary.arbitrary import io.kotest.property.arbitrary.choice -import io.kotest.property.arbitrary.filter import io.kotest.property.arbitrary.list import io.kotest.property.arbitrary.map import io.kotest.property.arbitrary.orNull @@ -38,9 +37,11 @@ fun Arb.Companion.option(arb: Arb): Arb> = arb.orNull().map { i fun Arb.Companion.either(left: Arb, right: Arb): Arb> = choice(left.map { Either.Left(it) }, right.map { Either.Right(it) }) -fun Arb.Companion.nonEmptyList(a: Arb): Arb> = list(a).filter(List::isNotEmpty).map { it.toNonEmptyListOrNull()!! } +@OptIn(PotentiallyUnsafeNonEmptyOperation::class) +fun Arb.Companion.nonEmptyList(a: Arb): Arb> = list(a, 1..100).map { it.wrapAsNonEmptyListOrThrow() } -fun Arb.Companion.nonEmptySet(a: Arb): Arb> = list(a).filter(List::isNotEmpty).map { it.toNonEmptySetOrNull()!! } +@OptIn(PotentiallyUnsafeNonEmptyOperation::class) +fun Arb.Companion.nonEmptySet(a: Arb): Arb> = nonEmptyList(a).map { it.toNonEmptySet() } fun Arb.Companion.ior(arbL: Arb, arbR: Arb): Arb> = Arb.choice( arbitrary { arbL.bind().leftIor() }, diff --git a/arrow-libs/integrations/arrow-core-serialization/src/commonTest/kotlin/arrow/core/serialization/Generators.kt b/arrow-libs/integrations/arrow-core-serialization/src/commonTest/kotlin/arrow/core/serialization/Generators.kt index b64bb0195dd..537e318fd44 100644 --- a/arrow-libs/integrations/arrow-core-serialization/src/commonTest/kotlin/arrow/core/serialization/Generators.kt +++ b/arrow-libs/integrations/arrow-core-serialization/src/commonTest/kotlin/arrow/core/serialization/Generators.kt @@ -13,8 +13,9 @@ import kotlin.math.max fun Arb.Companion.nonEmptyList(arb: Arb, range: IntRange = 0 .. 100): Arb> = Arb.bind(arb, Arb.list(arb, range)) { head, tail -> NonEmptyList(head, tail) } +@OptIn(PotentiallyUnsafeNonEmptyOperation::class) fun Arb.Companion.nonEmptySet(arb: Arb, range: IntRange = 0 .. 100): Arb> = - Arb.set(arb, max(range.first, 1) .. range.last).map { it.toNonEmptySetOrNull()!! } + Arb.set(arb, max(range.first, 1) .. range.last).map { it.wrapAsNonEmptySetOrThrow() } fun Arb.Companion.option(arb: Arb): Arb> = arb.orNull().map { it.toOption() } diff --git a/arrow-libs/optics/arrow-optics/src/commonTest/kotlin/arrow/optics/test/Generators.kt b/arrow-libs/optics/arrow-optics/src/commonTest/kotlin/arrow/optics/test/Generators.kt index 0d3ddd5ed4e..1ccfc8f4bc2 100644 --- a/arrow-libs/optics/arrow-optics/src/commonTest/kotlin/arrow/optics/test/Generators.kt +++ b/arrow-libs/optics/arrow-optics/src/commonTest/kotlin/arrow/optics/test/Generators.kt @@ -3,14 +3,15 @@ package arrow.optics.test import arrow.core.Either import arrow.core.NonEmptyList import arrow.core.Option +import arrow.core.PotentiallyUnsafeNonEmptyOperation import arrow.core.Tuple4 import arrow.core.Tuple5 import arrow.core.Tuple6 import arrow.core.Tuple7 import arrow.core.Tuple8 import arrow.core.Tuple9 -import arrow.core.toNonEmptyListOrNull import arrow.core.toOption +import arrow.core.wrapAsNonEmptyListOrThrow import io.kotest.property.Arb import io.kotest.property.arbitrary.bind import io.kotest.property.arbitrary.choice @@ -19,8 +20,9 @@ import io.kotest.property.arbitrary.map import io.kotest.property.arbitrary.orNull import kotlin.math.max +@OptIn(PotentiallyUnsafeNonEmptyOperation::class) fun Arb.Companion.nonEmptyList(arb: Arb, range: IntRange = 0 .. 100): Arb> = - Arb.list(arb, max(range.first, 1) .. range.last).map { it.toNonEmptyListOrNull()!! } + Arb.list(arb, max(range.first, 1) .. range.last).map { it.wrapAsNonEmptyListOrThrow() } fun Arb.Companion.sequence(arb: Arb, range: IntRange = 0 .. 100): Arb> = Arb.list(arb, range).map { it.asSequence() }