diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 5cc009322f9..252e60ea971 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -1572,6 +1572,8 @@ public final class arrow/core/SequenceKt { public static final fun crosswalk (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; public static final fun crosswalkMap (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Ljava/util/Map; public static final fun crosswalkNull (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; + public static final fun crosswalkNullList (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Ljava/util/List; + public static final fun crosswalkT (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Ljava/util/List; public static final fun filterOption (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun flatten (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun fold (Lkotlin/sequences/Sequence;Larrow/typeclasses/Monoid;)Ljava/lang/Object; @@ -1593,6 +1595,7 @@ public final class arrow/core/SequenceKt { public static final fun salign (Lkotlin/sequences/Sequence;Larrow/typeclasses/Semigroup;Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun salign (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function2;)Lkotlin/sequences/Sequence; public static final fun separateEither (Lkotlin/sequences/Sequence;)Lkotlin/Pair; + public static final fun separateEitherToPair (Lkotlin/sequences/Sequence;)Lkotlin/Pair; public static final fun separateValidated (Lkotlin/sequences/Sequence;)Lkotlin/Pair; public static final fun sequence (Lkotlin/sequences/Sequence;)Larrow/core/Either; public static final fun sequence (Lkotlin/sequences/Sequence;)Larrow/core/Option; @@ -1611,11 +1614,15 @@ public final class arrow/core/SequenceKt { public static final fun traverseValidated (Lkotlin/sequences/Sequence;Larrow/typeclasses/Semigroup;Lkotlin/jvm/functions/Function1;)Larrow/core/Validated; public static final fun unalign (Lkotlin/sequences/Sequence;)Lkotlin/Pair; public static final fun unalign (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair; + public static final fun unalignToPair (Lkotlin/sequences/Sequence;)Lkotlin/Pair; + public static final fun unalignToPair (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair; public static final fun uniteEither (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun uniteValidated (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun unweave (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/sequences/Sequence; public static final fun unzip (Lkotlin/sequences/Sequence;)Lkotlin/Pair; public static final fun unzip (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair; + public static final fun unzipToPair (Lkotlin/sequences/Sequence;)Lkotlin/Pair; + public static final fun unzipToPair (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/Pair; public static final fun void (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun widen (Lkotlin/sequences/Sequence;)Lkotlin/sequences/Sequence; public static final fun zip (Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function3;)Lkotlin/sequences/Sequence; diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt index b54517fe15f..a831815a9d4 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Sequence.kt @@ -1,5 +1,8 @@ @file:OptIn(ExperimentalTypeInference::class) +/** + * + */ package arrow.core import arrow.core.Either.Left @@ -7,11 +10,14 @@ import arrow.core.Either.Right import arrow.core.raise.RaiseAccumulate import arrow.core.raise.fold import arrow.typeclasses.Monoid +import arrow.typeclasses.MonoidDeprecation import arrow.typeclasses.Semigroup import arrow.typeclasses.SemigroupDeprecation import arrow.typeclasses.combine import kotlin.experimental.ExperimentalTypeInference +import kotlin.jvm.JvmName +/** Adds [kotlin.sequences.zip] support for 3 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -30,6 +36,7 @@ public fun Sequence.zip( } } +/** Adds [kotlin.sequences.zip] support for 4 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -50,6 +57,7 @@ public fun Sequence.zip( } } +/** Adds [kotlin.sequences.zip] support for 5 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -72,6 +80,7 @@ public fun Sequence.zip( } } +/** Adds [kotlin.sequences.zip] support for 6 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -96,6 +105,7 @@ public fun Sequence.zip( } } +/** Adds [kotlin.sequences.zip] support for 7 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -129,6 +139,7 @@ public fun Sequence.zip( } } +/** Adds [kotlin.sequences.zip] support for 8 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -165,6 +176,7 @@ public fun Sequence.zip( } } +/** Adds [kotlin.sequences.zip] support for 9 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -204,6 +216,7 @@ public fun Sequence.zip( } } +/** Adds [kotlin.sequences.zip] support for 10 parameters */ public fun Sequence.zip( c: Sequence, d: Sequence, @@ -247,60 +260,84 @@ public fun Sequence.zip( } /** - * Combines two structures by taking the union of their shapes and combining the elements with the given function. + * Combines two [Sequence] by returning [Ior.Both] when both [Sequence] have an item, + * [Ior.Left] when only the first [Sequence] has an item, + * and [Ior.Right] when only the second [Sequence] has an item. * * ```kotlin * import arrow.core.align + * import arrow.core.Ior + * import arrow.core.Ior.Both + * import arrow.core.Ior.Left + * import arrow.core.Ior.Right + * import io.kotest.matchers.shouldBe * - * fun main(args: Array) { - * //sampleStart - * val result = - * sequenceOf("A", "B").align(sequenceOf(1, 2, 3)) { - * "$it" - * } - * //sampleEnd - * println(result.toList()) + * fun test() { + * fun Ior.visualise(): String = + * fold({ "$it<" }, { ">$it" }, { a, b -> "$a<>$b" }) + * + * sequenceOf("A", "B").align(sequenceOf(1, 2, 3)) { ior -> + * ior.visualise() + * }.toList() shouldBe listOf("A<>1", "B<>2", ">3") + * + * sequenceOf("A", "B", "C").align(sequenceOf(1, 2)) { ior -> + * ior.visualise() + * }.toList() shouldBe listOf("A<>1", "B<>2", "C<") * } * ``` * + * */ -public fun Sequence.align(b: Sequence, fa: (Ior) -> C): Sequence = - this.align(b).map(fa) +public fun Sequence.align(seqB: Sequence, fa: (Ior) -> C): Sequence = + alignRec(this, seqB, { fa(Ior.Left(it)) }, { fa(Ior.Right(it)) }) { a, b -> fa(Ior.Both(a, b)) } /** - * Combines two structures by taking the union of their shapes and using Ior to hold the elements. + * Combines two [Sequence] by returning [Ior.Both] when both [Sequence] have an item, + * [Ior.Left] when only the first [Sequence] has an item, + * and [Ior.Right] when only the second [Sequence] has an item. * * ```kotlin * import arrow.core.align + * import arrow.core.Ior.Both + * import arrow.core.Ior.Left + * import arrow.core.Ior.Right + * import io.kotest.matchers.shouldBe * - * fun main(args: Array) { - * //sampleStart - * val result = - * sequenceOf("A", "B").align(sequenceOf(1, 2, 3)) - * //sampleEnd - * println(result.toList()) + * fun test() { + * sequenceOf("A", "B") + * .align(sequenceOf(1, 2, 3)).toList() shouldBe listOf(Both("A", 1), Both("B", 2), Right(3)) + * + * sequenceOf("A", "B", "C") + * .align(sequenceOf(1, 2)).toList() shouldBe listOf(Both("A", 1), Both("B", 2), Left("C")) * } * ``` * + * */ -public fun Sequence.align(b: Sequence): Sequence> = - alignRec(this, b) - -private fun alignRec(ls: Sequence, rs: Sequence): Sequence> { +public fun Sequence.align(seqB: Sequence): Sequence> = + alignRec(this, seqB, { Ior.Left(it) }, { Ior.Right(it) }) { a, b -> Ior.Both(a, b) } + +private fun alignRec( + ls: Sequence, + rs: Sequence, + left: (X) -> Z, + right: (Y) -> Z, + both: (X, Y) -> Z +): Sequence { val lsIterator = ls.iterator() val rsIterator = rs.iterator() return sequence { while (lsIterator.hasNext() && rsIterator.hasNext()) { yield( - Ior.Both( + both( lsIterator.next(), rsIterator.next() ) ) } - while (lsIterator.hasNext()) yield(lsIterator.next().leftIor()) - while (rsIterator.hasNext()) yield(rsIterator.next().rightIor()) + while (lsIterator.hasNext()) yield(left(lsIterator.next())) + while (rsIterator.hasNext()) yield(right(rsIterator.next())) } } @@ -308,6 +345,10 @@ private fun alignRec(ls: Sequence, rs: Sequence): Sequence Sequence.combineAll(MA: Monoid): A = fold(MA) +@Deprecated( + "This function is actually terminal. Use crosswalk(f:A -> List) instead.", + ReplaceWith("this.crosswalk{a -> f(a).toList()}") +) public fun Sequence.crosswalk(f: (A) -> Sequence): Sequence> = fold(emptySequence()) { bs, a -> f(a).align(bs) { ior -> @@ -319,6 +360,24 @@ public fun Sequence.crosswalk(f: (A) -> Sequence): Sequence Sequence.crosswalk(f: (A) -> Iterable): List> = + fold(emptyList()) { bs, a -> + f(a).align(bs) { ior -> + ior.fold( + { listOf(it) }, + ::identity, + { l, r -> listOf(l) + r } + ) + } + } + + +@Deprecated( + "This function is actually terminal. Use crosswalk(f:A -> List) instead.", + ReplaceWith("this.crosswalk{a -> f(a).toList()}") +) public fun Sequence.crosswalkMap(f: (A) -> Map): Map> = fold(emptyMap()) { bs, a -> f(a).align(bs) { (_, ior) -> @@ -330,6 +389,10 @@ public fun Sequence.crosswalkMap(f: (A) -> Map): Map f(a).toList()}") +) public fun Sequence.crosswalkNull(f: (A) -> B?): Sequence? = fold?>(emptySequence()) { bs, a -> Ior.fromNullables(f(a), bs)?.fold( @@ -339,15 +402,32 @@ public fun Sequence.crosswalkNull(f: (A) -> B?): Sequence? = ) } +public fun Sequence.crosswalkNullList(f: (A) -> B?): List? = + fold?>(emptyList()) { bs, a -> + Ior.fromNullables(f(a), bs)?.fold( + { listOf(it) }, + ::identity, + { l, r -> listOf(l) + r } + ) + } + public fun Sequence>.flatten(): Sequence = flatMap(::identity) +@Deprecated( + "$MonoidDeprecation\n$NicheAPI", + ReplaceWith("this.fold(initial){ acc, a -> acc + a }", "arrow.core.sequence") +) public fun Sequence.fold(MA: Monoid): A = MA.run { this@fold.fold(empty()) { acc, a -> acc.combine(a) } } +@Deprecated( + "$MonoidDeprecation\n$NicheAPI", + ReplaceWith("fold(initial){ acc, a -> acc, f(a) } }", "arrow.core.sequence") +) public fun Sequence.foldMap(MB: Monoid, f: (A) -> B): B = MB.run { this@foldMap.fold(empty()) { acc, a -> acc.combine(f(a)) @@ -374,27 +454,30 @@ public fun Sequence.foldMap(MB: Monoid, f: (A) -> B): B = MB.run { * ``` * */ +@Deprecated( + "Use flatMap and ifEmpty instead.\n$NicheAPI", + ReplaceWith("flatMap(ffa).ifEmpty { fb }") +) public fun Sequence.ifThen(fb: Sequence, ffa: (A) -> Sequence): Sequence = - split()?.let { (fa, a) -> - ffa(a) + fa.flatMap(ffa) - } ?: fb + flatMap(ffa).ifEmpty { fb } /** - * interleave both computations in a fair way. + * Interleaves the elements of `this` [Sequence] with those of [other] [Sequence]. + * Elements of `this` and [other] are taken in turn, and the resulting list is the concatenation of the interleaved elements. + * If one [Sequence] is longer than the other, the remaining elements are appended to the end. * * ```kotlin - * import arrow.core.interleave + * import arrow.core.* + * import io.kotest.matchers.shouldBe * - * fun main(args: Array) { - * //sampleStart - * val tags = generateSequence { "#" }.take(10) - * val result = - * tags.interleave(sequenceOf("A", "B", "C")) - * //sampleEnd - * println(result.toList()) + * fun test() { + * val tags = generateSequence { "#" }.take(5) + * val numbers = generateSequence(0) { it + 1 }.take(3) + * tags.interleave(numbers).toList() shouldBe listOf("#", 0, "#", 1, "#", 2, "#", "#") * } * ``` * + * */ public fun Sequence.interleave(other: Sequence): Sequence = sequence { @@ -410,7 +493,7 @@ public fun Sequence.interleave(other: Sequence): Sequence = } /** - * Returns a [Sequence] containing the result of applying some transformation `(A?, B) -> C` + * Returns a [Sequence] containing the result of applying some transformation `(A?, B) -> C` * on a zip, excluding all cases where the right value is null. * * Example: @@ -488,19 +571,17 @@ public fun Sequence.once(): Sequence = * */ public fun Sequence.padZip(other: Sequence): Sequence> = - align(other) { ior -> - ior.fold( - { it to null }, - { null to it }, - { a, b -> a to b } - ) - } + alignRec( + this, + other, + { a -> Pair(a, null) }, + { b -> Pair(null, b) }, + { a, b -> Pair(a, b) } + ) /** - * Returns a [Sequence] containing the result of applying some transformation `(A?, B?) -> C` - * on a zip. + * Returns a [Sequence] containing the result of applying some transformation `(A?, B?) -> C` on a zip. * - * Example: * ```kotlin * import arrow.core.padZip * @@ -519,11 +600,17 @@ public fun Sequence.padZip(other: Sequence): Sequence> * */ public fun Sequence.padZip(other: Sequence, fa: (A?, B?) -> C): Sequence = - padZip(other).map { fa(it.first, it.second) } + alignRec( + this, + other, + { a -> fa(a, null) }, + { b -> fa(null, b) }, + { a, b -> fa(a, b) } + ) @Deprecated( "$SemigroupDeprecation\n$NicheAPI", - ReplaceWith("Sequence { List(n) { this@replicate }.iterator() }") + ReplaceWith("Sequence> { List>(n) { this }.iterator() }") ) public fun Sequence.replicate(n: Int): Sequence> = Sequence { List(n) { this@replicate }.iterator() } @@ -595,7 +682,7 @@ public fun Sequence.salign( /** * aligns two structures and combine them with the given [Semigroup.combine] */ -@Deprecated(SemigroupDeprecation, ReplaceWith("salign(other, SG::combine)", "arrow.typeclasses.combine")) +@Deprecated(SemigroupDeprecation, ReplaceWith("salign(other, {a, b -> a + b})", "arrow.typeclasses.combine")) public fun Sequence.salign( SG: Semigroup, other: Sequence @@ -608,6 +695,10 @@ public fun Sequence.salign( * @receiver Iterable of Validated * @return a tuple containing Sequence with [Either.Left] and another Sequence with its [Either.Right] values. */ +@Deprecated( + "This function is actually terminal. Use separateEitherToPair instead.", + ReplaceWith("separateEitherToPair()") +) public fun Sequence>.separateEither(): Pair, Sequence> = fold(sequenceOf() to sequenceOf()) { (lefts, rights), either -> when (either) { @@ -616,12 +707,26 @@ public fun Sequence>.separateEither(): Pair, Seq } } +public fun Sequence>.separateEitherToPair(): Pair, List> = + fold(listOf() to listOf()) { (lefts, rights), either -> + when (either) { + is Left -> lefts + either.value to rights + is Right -> lefts to rights + either.value + } + } + + /** * Separate the inner [Validated] values into the [Validated.Invalid] and [Validated.Valid]. * * @receiver Iterable of Validated * @return a tuple containing Sequence with [Validated.Invalid] and another Sequence with its [Validated.Valid] values. */ +@Deprecated( + "${ValidatedDeprMsg}SemigroupDeprecation\n$NicheAPI", + ReplaceWith("separateEither()") +) + public fun Sequence>.separateValidated(): Pair, Sequence> = fold(sequenceOf() to sequenceOf()) { (invalids, valids), validated -> when (validated) { @@ -678,21 +783,25 @@ public fun Sequence.some(): Sequence> = * * ```kotlin * import arrow.core.split + * import io.kotest.matchers.shouldBe * - * fun main(args: Array) { - * //sampleStart - * val result = sequenceOf("A", "B", "C").split() - * //sampleEnd - * result?.let { println("(${it.first.toList()}, ${it.second.toList()})") } + * fun test() { + * sequenceOf("A", "B", "C").split()?.let { (tail, head) -> + * head shouldBe "A" + * tail.toList() shouldBe listOf("B", "C") + * } + * emptySequence().split() shouldBe null * } * ``` * + * */ public fun Sequence.split(): Pair, A>? = firstOrNull()?.let { first -> Pair(tail(), first) } +/** Alias for drop(1) */ public fun Sequence.tail(): Sequence = drop(1) @@ -745,9 +854,8 @@ public fun Sequence.traverseOption(f: (A) -> Option): Option({e1, e2 -> e1 + e2}) { f(it).bind() }.toValidated()", + "arrow.core.mapOrAccumulate" ) ) @OptIn(ExperimentalTypeInference::class) @@ -781,7 +889,10 @@ public fun Sequence.mapOrAccumulate( @Deprecated( "traverseValidated is being renamed to traverse to simplify the Arrow API", - ReplaceWith("traverse(semigroup, f).map { it.asSequence() }", "arrow.core.traverse") + ReplaceWith( + "mapOrAccumulate{e1, e2 -> e1 + e2} { f(it).bind() }.toValidated().map { it.asSequence() }", + "`arrow.core.mapOrAccumulate`" + ) ) public fun Sequence.traverseValidated( semigroup: Semigroup, @@ -789,25 +900,38 @@ public fun Sequence.traverseValidated( ): Validated> = traverse(semigroup, f).map { it.asSequence() } +@Deprecated( + "This function is actually terminal. Use unalignToPair instead.", + ReplaceWith("unalignToPair()") +) +public fun Sequence>.unalign(): Pair, Sequence> = + fold(emptySequence() to emptySequence()) { (l, r), x -> + x.fold( + { l + it to r }, + { l to r + it }, + { a, b -> l + a to r + b } + ) + } + /** * splits an union into its component parts. * * ```kotlin * import arrow.core.bothIor * import arrow.core.leftIor - * import arrow.core.unalign + * import arrow.core.unalignToPair * * fun main(args: Array) { * //sampleStart - * val result = sequenceOf(("A" to 1).bothIor(), ("B" to 2).bothIor(), "C".leftIor()).unalign() + * val result = sequenceOf(("A" to 1).bothIor(), ("B" to 2).bothIor(), "C".leftIor()).unalignToPair() * //sampleEnd - * println("(${result.first.toList()}, ${result.second.toList()})") + * println("(${result.first}, ${result.second})") * } * ``` * */ -public fun Sequence>.unalign(): Pair, Sequence> = - fold(emptySequence() to emptySequence()) { (l, r), x -> +public fun Sequence>.unalignToPair(): Pair, List> = + fold(emptyList() to emptyList()) { (l, r), x -> x.fold( { l + it to r }, { l to r + it }, @@ -815,30 +939,51 @@ public fun Sequence>.unalign(): Pair, Sequence> ) } + +@Deprecated( + "This function is actually terminal. Use unalignToPair instead.", + ReplaceWith("unalignToPair(fa)") +) +public fun Sequence.unalign(fa: (C) -> Ior): Pair, Sequence> = + map(fa).unalign() + /** * after applying the given function, splits the resulting union shaped structure into its components parts * * ```kotlin * import arrow.core.leftIor - * import arrow.core.unalign + * import arrow.core.unalignToPair * * fun main(args: Array) { * //sampleStart - * val result = sequenceOf(1, 2, 3).unalign { it.leftIor() } + * val result = sequenceOf(1, 2, 3).unalignToPair { it.leftIor() } * //sampleEnd * println("(${result.first.toList()}, ${result.second.toList()})") * } * ``` * */ -public fun Sequence.unalign(fa: (C) -> Ior): Pair, Sequence> = - map(fa).unalign() +public fun Sequence.unalignToPair(fa: (C) -> Ior): Pair, List> = + map(fa).unalignToPair() +@Deprecated( + NicheAPI + "Prefer using flatMap + fold", + ReplaceWith( + "flatMap { either -> either.fold>({ emptySequence() }, { b -> sequenceOf(b) }) }" + ) +) public fun Sequence>.uniteEither(): Sequence = flatMap { either -> either.fold({ emptySequence() }, { b -> sequenceOf(b) }) } +@Deprecated( + ValidatedDeprMsg, + ReplaceWith( + "flatMap { validated -> validated.toEither().fold>({ emptySequence() }, { b -> sequenceOf(b) })}", + "arrow.core.traverse" + ) +) public fun Sequence>.uniteValidated(): Sequence = flatMap { validated -> validated.fold({ emptySequence() }, { b -> sequenceOf(b) }) @@ -864,71 +1009,114 @@ public fun Sequence.unweave(ffa: (A) -> Sequence): Sequence = ffa(a).interleave(fa.unweave(ffa)) } ?: emptySequence() +@Deprecated( + "This function is actually terminal. Use unzipToPair instead.", + ReplaceWith("unzipToPair()") +) +public fun Sequence>.unzip(): Pair, Sequence> = + fold(emptySequence() to emptySequence()) { (l, r), x -> + l + x.first to r + x.second + } + /** * unzips the structure holding the resulting elements in an `Pair` * * ```kotlin - * import arrow.core.unzip + * import arrow.core.unzipToPair * * fun main(args: Array) { * //sampleStart - * val result = sequenceOf("A" to 1, "B" to 2).unzip() + * val result = sequenceOf("A" to 1, "B" to 2).unzipToPair() * //sampleEnd - * println("(${result.first.toList()}, ${result.second.toList()})") + * println("(${result.first}, ${result.second})") * } * ``` * */ -public fun Sequence>.unzip(): Pair, Sequence> = - fold(emptySequence() to emptySequence()) { (l, r), x -> +public fun Sequence>.unzipToPair(): Pair, List> = + fold(emptyList() to emptyList()) { (l, r), x -> l + x.first to r + x.second } + +@Deprecated( + "This function is actually terminal. Use unzipToPair instead.", + ReplaceWith("unzipToPair(fc)") +) +public fun Sequence.unzip(fc: (C) -> Pair): Pair, Sequence> = + map(fc).unzip() + /** * after applying the given function unzip the resulting structure into its elements. * * ```kotlin - * import arrow.core.unzip + * import arrow.core.unzipToPair * * fun main(args: Array) { * //sampleStart * val result = - * sequenceOf("A:1", "B:2", "C:3").unzip { e -> + * sequenceOf("A:1", "B:2", "C:3").unzipToPair { e -> * e.split(":").let { * it.first() to it.last() * } * } * //sampleEnd - * println("(${result.first.toList()}, ${result.second.toList()})") + * println("(${result.first}, ${result.second})") * } * ``` * */ -public fun Sequence.unzip(fc: (C) -> Pair): Pair, Sequence> = - map(fc).unzip() +public fun Sequence.unzipToPair(fc: (C) -> Pair): Pair, List> = + map(fc).unzipToPair() + +@Deprecated( + "void is being deprecated in favor of simple Iterable.map.\n$NicheAPI", + ReplaceWith("map { }") +) public fun Sequence.void(): Sequence = map { Unit } /** - * Given [A] is a sub type of [B], re-type this value from Sequence to Sequence + * Given [A] is a subtype of [B], re-type this value from Sequence to Sequence * - * Kind -> Kind - * - * ```kotlin - * import arrow.core.widen + * ```kotlin + * import arrow.core.widen * - * fun main(args: Array) { - * //sampleStart - * val result: Sequence = - * sequenceOf("Hello World").widen() - * //sampleEnd - * println(result) - * } - * ``` + * fun main(args: Array) { + * val original: Sequence = sequenceOf("Hello World") + * val result: Sequence = original.widen() + * } + * ``` + * */ public fun Sequence.widen(): Sequence = this +/** + * Filters out all elements that are [None], + * and unwraps the remaining elements [Some] values. + * + * ```kotlin + * import arrow.core.None + * import arrow.core.Some + * import arrow.core.filterOption + * import io.kotest.matchers.shouldBe + * + * fun test() { + * generateSequence(0) { it + 1 } + * .map { if (it % 2 == 0) Some(it) else None } + * .filterOption() + * .take(5) + * .toList() shouldBe listOf(0, 2, 4, 6, 8) + * } + * ``` + * + * + */ public fun Sequence>.filterOption(): Sequence = - mapNotNull { it.orNull() } + sequence { + forEach { option -> + option.fold({ }, { a -> yield(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 bf8b1d52ea1..b8b3d2a77cd 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 @@ -12,6 +12,7 @@ import io.kotest.property.checkAll import io.kotest.matchers.shouldBe import io.kotest.property.arbitrary.int import io.kotest.property.arbitrary.list +import io.kotest.property.arbitrary.pair import io.kotest.property.arbitrary.positiveInt import io.kotest.property.arbitrary.string import kotlin.math.max @@ -40,24 +41,26 @@ class SequenceKTest : StringSpec({ // also verifies result order and execution order (l to r) val acc = mutableListOf() val res = generateSequence(0) { it + 1 }.traverse { a -> - (a <= 20_000).maybe { + if ((a <= 20_000)) { acc.add(a) - a + Some(a) + } else { + None } } acc shouldBe (0..20_000).toList() res shouldBe None } - "traverse for Validated stack-safe" { + "mapOrAccumlate for Either stack-safe" { // also verifies result order and execution order (l to r) val acc = mutableListOf() - val res = (0..20_000).asSequence().traverse(Semigroup.string()) { + val res = (0..20_000).asSequence().mapOrAccumulate(String::plus) { acc.add(it) - Validated.Valid(it) + Either.Right(it).bind() }.map { it.toList() } - res shouldBe Validated.Valid(acc) - res shouldBe Validated.Valid((0..20_000).toList()) + res shouldBe Either.Right(acc) + res shouldBe Either.Right((0..20_000).toList()) } "traverse for Validated acummulates" { @@ -235,6 +238,25 @@ class SequenceKTest : StringSpec({ } } + "crosswalk the sequence to a List function" { + checkAll(Arb.list(Arb.string())){ strList -> + val obtained = strList.asSequence().crosswalk { listOf(it.length) } + val expected = if (strList.isEmpty()) emptyList() + else listOf(strList.map { it.length }) + obtained.map{ it.sorted() } shouldBe expected.map { it.sorted() } + } + } + + "crosswalk the sequence to a nullable function" { + checkAll(Arb.list(Arb.string())){ strList -> + fun nullEvens(i: Int): Int? = if(i % 2 == 0) i else null + + val obtained = strList.asSequence().crosswalkNullList { nullEvens(it.length) } + val expected = strList.map { nullEvens(it.length) } + obtained?.size shouldBe expected.filterNotNull().size + } + } + "can align sequences - 1" { checkAll(Arb.sequence(Arb.int()), Arb.sequence(Arb.string())) { a, b -> a.align(b).toList().size shouldBe max(a.toList().size, b.toList().size) @@ -244,7 +266,7 @@ class SequenceKTest : StringSpec({ "can align sequences - 2" { checkAll(Arb.sequence(Arb.int()), Arb.sequence(Arb.string())) { a, b -> a.align(b).take(min(a.toList().size, b.toList().size)).forEach { - it.isBoth shouldBe true + it.isBoth() shouldBe true } } } @@ -254,11 +276,8 @@ class SequenceKTest : StringSpec({ val ls = a.toList() val rs = b.toList() a.align(b).drop(min(ls.size, rs.size)).forEach { - if (ls.size < rs.size) { - it.isRight shouldBe true - } else { - it.isLeft shouldBe true - } + if (ls.size < rs.size) it.isRight() shouldBe true + else it.isLeft() shouldBe true } } } @@ -302,7 +321,24 @@ class SequenceKTest : StringSpec({ } } - "filterOption should filter None" { + "unzipToPair should unzip values in a Pair in a Sequence of Pairs" { + checkAll(Arb.list(Arb.pair(Arb.string(), Arb.int()))){ pairList -> + val obtained = pairList.asSequence().unzipToPair() + val expected = pairList.unzip() + obtained shouldBe expected + } + } + + "unzipToPair should unzip values in a Pair in a Sequence" { + checkAll(Arb.list(Arb.string())){ strList -> + val obtained = strList.asSequence().unzipToPair { str -> Pair(str, str.length)} + val expected = strList.unzip { str -> Pair(str, str.length) } + obtained shouldBe expected + } + } + + + "filterOption should filter None" { checkAll(Arb.list(Arb.option(Arb.int()))) { ints -> ints.asSequence().filterOption().toList() shouldBe ints.filterOption() } @@ -315,20 +351,20 @@ class SequenceKTest : StringSpec({ else it.right() } - val (lefts, rights) = sequence.separateEither() + val (lefts, rights) = sequence.separateEitherToPair() - lefts.toList() to rights.toList() shouldBe ints.partition { it % 2 == 0 } + lefts to rights shouldBe ints.partition { it % 2 == 0 } } } "separateValidated" { checkAll(Arb.sequence(Arb.int())) { ints -> val sequence = ints.map { - if (it % 2 == 0) it.invalid() - else it.valid() + if (it % 2 == 0) it.left() + else it.right() } - val (invalids, valids) = sequence.separateValidated() + val (invalids, valids) = sequence.separateEitherToPair() invalids.toList() to valids.toList() shouldBe ints.partition { it % 2 == 0 } } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-01.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-01.kt index 12a2be59d90..6257919c325 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-01.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-01.kt @@ -2,13 +2,21 @@ package arrow.core.examples.exampleSequence01 import arrow.core.align +import arrow.core.Ior +import arrow.core.Ior.Both +import arrow.core.Ior.Left +import arrow.core.Ior.Right +import io.kotest.matchers.shouldBe -fun main(args: Array) { - //sampleStart - val result = - sequenceOf("A", "B").align(sequenceOf(1, 2, 3)) { - "$it" - } - //sampleEnd - println(result.toList()) +fun test() { + fun Ior.visualise(): String = + fold({ "$it<" }, { ">$it" }, { a, b -> "$a<>$b" }) + + sequenceOf("A", "B").align(sequenceOf(1, 2, 3)) { ior -> + ior.visualise() + }.toList() shouldBe listOf("A<>1", "B<>2", ">3") + + sequenceOf("A", "B", "C").align(sequenceOf(1, 2)) { ior -> + ior.visualise() + }.toList() shouldBe listOf("A<>1", "B<>2", "C<") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-02.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-02.kt index 9566c93918d..d2a7f9f10a6 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-02.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-02.kt @@ -2,11 +2,15 @@ package arrow.core.examples.exampleSequence02 import arrow.core.align +import arrow.core.Ior.Both +import arrow.core.Ior.Left +import arrow.core.Ior.Right +import io.kotest.matchers.shouldBe -fun main(args: Array) { - //sampleStart - val result = - sequenceOf("A", "B").align(sequenceOf(1, 2, 3)) - //sampleEnd - println(result.toList()) +fun test() { + sequenceOf("A", "B") + .align(sequenceOf(1, 2, 3)).toList() shouldBe listOf(Both("A", 1), Both("B", 2), Right(3)) + + sequenceOf("A", "B", "C") + .align(sequenceOf(1, 2)).toList() shouldBe listOf(Both("A", 1), Both("B", 2), Left("C")) } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-04.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-04.kt index aa0ab0e390d..16dcbebfe71 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-04.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-04.kt @@ -1,13 +1,11 @@ // This file was automatically generated from Sequence.kt by Knit tool. Do not edit. package arrow.core.examples.exampleSequence04 -import arrow.core.interleave +import arrow.core.* +import io.kotest.matchers.shouldBe -fun main(args: Array) { - //sampleStart - val tags = generateSequence { "#" }.take(10) - val result = - tags.interleave(sequenceOf("A", "B", "C")) - //sampleEnd - println(result.toList()) +fun test() { + val tags = generateSequence { "#" }.take(5) + val numbers = generateSequence(0) { it + 1 }.take(3) + tags.interleave(numbers).toList() shouldBe listOf("#", 0, "#", 1, "#", 2, "#", "#") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-11.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-11.kt index 0e7ce3cdaaf..dac1899f073 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-11.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-11.kt @@ -2,10 +2,12 @@ package arrow.core.examples.exampleSequence11 import arrow.core.split +import io.kotest.matchers.shouldBe -fun main(args: Array) { - //sampleStart - val result = sequenceOf("A", "B", "C").split() - //sampleEnd - result?.let { println("(${it.first.toList()}, ${it.second.toList()})") } +fun test() { + sequenceOf("A", "B", "C").split()?.let { (tail, head) -> + head shouldBe "A" + tail.toList() shouldBe listOf("B", "C") + } + emptySequence().split() shouldBe null } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-12.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-12.kt index 3ad3a8684f2..4d3194f9f85 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-12.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-12.kt @@ -3,11 +3,11 @@ package arrow.core.examples.exampleSequence12 import arrow.core.bothIor import arrow.core.leftIor -import arrow.core.unalign +import arrow.core.unalignToPair fun main(args: Array) { //sampleStart - val result = sequenceOf(("A" to 1).bothIor(), ("B" to 2).bothIor(), "C".leftIor()).unalign() + val result = sequenceOf(("A" to 1).bothIor(), ("B" to 2).bothIor(), "C".leftIor()).unalignToPair() //sampleEnd - println("(${result.first.toList()}, ${result.second.toList()})") + println("(${result.first}, ${result.second})") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-13.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-13.kt index 5f18e813bc1..6c530ffda5e 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-13.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-13.kt @@ -2,11 +2,11 @@ package arrow.core.examples.exampleSequence13 import arrow.core.leftIor -import arrow.core.unalign +import arrow.core.unalignToPair fun main(args: Array) { //sampleStart - val result = sequenceOf(1, 2, 3).unalign { it.leftIor() } + val result = sequenceOf(1, 2, 3).unalignToPair { it.leftIor() } //sampleEnd println("(${result.first.toList()}, ${result.second.toList()})") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-15.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-15.kt index b5f5cf116ae..e84c1ad1a05 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-15.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-15.kt @@ -1,11 +1,11 @@ // This file was automatically generated from Sequence.kt by Knit tool. Do not edit. package arrow.core.examples.exampleSequence15 -import arrow.core.unzip +import arrow.core.unzipToPair fun main(args: Array) { //sampleStart - val result = sequenceOf("A" to 1, "B" to 2).unzip() + val result = sequenceOf("A" to 1, "B" to 2).unzipToPair() //sampleEnd - println("(${result.first.toList()}, ${result.second.toList()})") + println("(${result.first}, ${result.second})") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-16.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-16.kt index 4a8ab2e451d..6baf2ab5141 100644 --- a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-16.kt +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-16.kt @@ -1,16 +1,16 @@ // This file was automatically generated from Sequence.kt by Knit tool. Do not edit. package arrow.core.examples.exampleSequence16 -import arrow.core.unzip +import arrow.core.unzipToPair fun main(args: Array) { //sampleStart val result = - sequenceOf("A:1", "B:2", "C:3").unzip { e -> + sequenceOf("A:1", "B:2", "C:3").unzipToPair { e -> e.split(":").let { it.first() to it.last() } } //sampleEnd - println("(${result.first.toList()}, ${result.second.toList()})") + println("(${result.first}, ${result.second})") } diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-17.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-17.kt new file mode 100644 index 00000000000..f08eec01d58 --- /dev/null +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-17.kt @@ -0,0 +1,9 @@ +// This file was automatically generated from Sequence.kt by Knit tool. Do not edit. +package arrow.core.examples.exampleSequence17 + +import arrow.core.widen + +fun main(args: Array) { + val original: Sequence = sequenceOf("Hello World") + val result: Sequence = original.widen() +} diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-18.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-18.kt new file mode 100644 index 00000000000..80df0bf6c81 --- /dev/null +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/example-sequence-18.kt @@ -0,0 +1,15 @@ +// This file was automatically generated from Sequence.kt by Knit tool. Do not edit. +package arrow.core.examples.exampleSequence18 + +import arrow.core.None +import arrow.core.Some +import arrow.core.filterOption +import io.kotest.matchers.shouldBe + +fun test() { +generateSequence(0) { it + 1 } + .map { if (it % 2 == 0) Some(it) else None } + .filterOption() + .take(5) + .toList() shouldBe listOf(0, 2, 4, 6, 8) +} diff --git a/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/test/SequenceKnitTest.kt b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/test/SequenceKnitTest.kt new file mode 100644 index 00000000000..2ad713d464f --- /dev/null +++ b/arrow-libs/core/arrow-core/src/jvmTest/kotlin/examples/test/SequenceKnitTest.kt @@ -0,0 +1,29 @@ +// This file was automatically generated from Sequence.kt by Knit tool. Do not edit. +package arrow.core.examples.test + +import io.kotest.core.spec.style.StringSpec + +class SequenceKnitTest : StringSpec({ + "ExampleSequence01" { + arrow.core.examples.exampleSequence01.test() + } + + "ExampleSequence02" { + arrow.core.examples.exampleSequence02.test() + } + + "ExampleSequence04" { + arrow.core.examples.exampleSequence04.test() + } + + "ExampleSequence11" { + arrow.core.examples.exampleSequence11.test() + } + + "ExampleSequence18" { + arrow.core.examples.exampleSequence18.test() + } + +}) { + override fun timeout(): Long = 1000 +}