diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt index 5ad62be1217..5203f30d59e 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Iterable.kt @@ -683,11 +683,11 @@ public inline fun Iterable.unzip(fc: (C) -> Pair): Pair */ -public fun Iterable>.unalign(): Pair, List> = +public fun Iterable>.unalign(): Pair, List> = fold(emptyList() to emptyList()) { (l, r), x -> x.fold( - { l + it to r }, - { l to r + it }, + { l + it to r + null}, + { l + null to r + it }, { a, b -> l + a to r + b } ) } @@ -710,7 +710,7 @@ public fun Iterable>.unalign(): Pair, List> = * ``` * */ -public inline fun Iterable.unalign(fa: (C) -> Ior): Pair, List> = +public inline fun Iterable.unalign(fa: (C) -> Ior): Pair, List> = map(fa).unalign() @Deprecated("use fold instead", ReplaceWith("fold(MA)", "arrow.core.fold")) 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 e01b46dc382..934cc976bd4 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 @@ -1,6 +1,7 @@ package arrow.core import arrow.core.test.either +import arrow.core.test.ior import arrow.core.test.option import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.nulls.shouldNotBeNull @@ -301,4 +302,34 @@ class IterableTest : StringSpec({ } } + "unalign is the inverse of align" { + fun Pair,List>.fix(): Pair, List> = first.mapNotNull { it } to second.mapNotNull { it } + + checkAll(Arb.list(Arb.int()), Arb.list(Arb.string())) { a, b -> + a.align(b).unalign().fix() shouldBe (a to b) + } + } + + "align is the inverse of unalign" { + fun Ior.fix(): Ior = fold({ Ior.Left(it!!) }, { Ior.Right(it!!) }, { a, b -> + when { + a == null -> Ior.Right(b!!) + b == null -> Ior.Left(a) + else -> Ior.Both(a, b) + } + }) + + checkAll(Arb.list(Arb.ior(Arb.int(), Arb.string()))) { xs -> + val (a, b) = xs.unalign() + a.align(b) { + it.fix() + } shouldBe xs + } + } + + "unalign(fn)" { + checkAll(Arb.list(Arb.ior(Arb.int(), Arb.string()))) { xs -> + xs.unalign { it } shouldBe xs.unalign() + } + } }) 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 9847a8faf2a..534661b94ac 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 @@ -77,7 +77,12 @@ fun Arb.Companion.ior(arbA: Arb, arbB: Arb): Arb> = arbA.alignWith(arbB) { it } private fun Arb.alignWith(arbB: Arb, transform: (Ior) -> R): Arb = - Arb.bind(this, arbB) { a, b -> transform(Ior.Both(a, b)) } + Arb.choice( + this.map { Ior.Left(it) }, + Arb.bind(this, arbB) { a, b -> Ior.Both(a, b) }, + arbB.map { Ior.Right(it) } + ).map(transform) + fun Arb.Companion.suspendFunThatReturnsEitherAnyOrAnyOrThrows(): Arb Either> = choice( diff --git a/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/test/GeneratorsTest.kt b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/test/GeneratorsTest.kt new file mode 100644 index 00000000000..d72517fda12 --- /dev/null +++ b/arrow-libs/core/arrow-core/src/commonTest/kotlin/arrow/core/test/GeneratorsTest.kt @@ -0,0 +1,27 @@ +package arrow.core.test + +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.StringSpec +import io.kotest.inspectors.forAtLeastOne +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.property.Arb +import io.kotest.property.arbitrary.int +import io.kotest.property.arbitrary.list +import io.kotest.property.arbitrary.next +import io.kotest.property.arbitrary.string + +class GeneratorsTest : StringSpec({ + "Arb.ior should generate Left, Right & Both" { + assertSoftly(Arb.list(Arb.ior(Arb.string(), Arb.int())).next()) { + forAtLeastOne { + it.isRight.shouldBeTrue() + } + forAtLeastOne { + it.isBoth.shouldBeTrue() + } + forAtLeastOne { + it.isLeft.shouldBeTrue() + } + } + } +})