The Typeclass Foldable
provide us the ability, given a type Kind<F, A>
, to aggregate their values A
.
Foldable<F>
is implemented in terms of two basic methods:
fa.foldLeft(init, f)
eagerly folds fa
from left-to-right.fa.foldRight(init, f)
lazily folds fa
from right-to-left.Beyond these, it provides many other useful methods related to folding over Kind<F, A>
values.
For the following examples, we are going to use some common imports.
import arrow.Kind
import arrow.core.*
import arrow.core.ListK
import arrow.core.k
import arrow.core.extensions.monoid
import arrow.core.extensions.listk.foldable.foldable
import arrow.core.extensions.option.foldable.foldable
import arrow.typeclasses.Foldable
And we’ll use the same two variables to see the different behaviors of Foldable
:
val maybeStr: Option<String> = Some("abc")
val strList: ListK<String> = listOf("a", "b", "c").k()
Left associative fold on F
using the provided function.
fun <F> concatenateStringFromLeft(strKind: Kind<F, String>, FO: Foldable<F>): String =
FO.run {
strKind.foldLeft("str: ") { base: String, value: String -> base + value }
}
concatenateStringFromLeft(maybeStr, Option.foldable())
// str: abc
concatenateStringFromLeft(None, Option.foldable())
// str:
concatenateStringFromLeft(strList, ListK.foldable())
// str: abc
Right associative lazy fold on F
using the provided function.
This method evaluates lb
lazily, and returns a lazy value to support laziness in a stack-safe way avoiding StackOverflows.
For more detailed information about how this method works, see the documentation for Eval<A>
.
fun <F> concatenateStringFromRight(strKind: Kind<F, String>, FO: Foldable<F>): String =
FO.run {
strKind.foldRight(Eval.now("str: ")) { value: String, base: Eval<String> -> base.map { it + value } }
.value()
}
concatenateStringFromRight(maybeStr, Option.foldable())
// str: abc
concatenateStringFromRight(None, Option.foldable())
// str:
concatenateStringFromRight(strList, ListK.foldable())
// str: cba
Fold implemented using the given Monoid<A>
instance.
fun <F> concatenateString(strKind: Kind<F, String>, FO: Foldable<F>): String =
FO.run {
"str: " + strKind.fold(String.monoid())
}
concatenateString(maybeStr, Option.foldable())
// str: abc
concatenateString(None, Option.foldable())
// str:
concatenateString(strList, ListK.foldable())
// str: abc
Alternatively, we have combineAll
, which is an alias for fold.
fun <F> combineAllString(strKind: Kind<F, String>, FO: Foldable<F>): String =
FO.run {
"str: " + strKind.combineAll(String.monoid())
}
combineAllString(maybeStr, Option.foldable())
// str: abc
combineAllString(None, Option.foldable())
// str:
combineAllString(strList, ListK.foldable())
// str: abc
fun <F> reduceLeftToOption(strKind: Kind<F, String>, FO: Foldable<F>): Option<Int> =
FO.run {
strKind.reduceLeftToOption({ it.length }) { base: Int, value: String -> base + value.length }
}
reduceLeftToOption(maybeStr, Option.foldable())
// Option.Some(3)
reduceLeftToOption(None, Option.foldable())
// Option.None
reduceLeftToOption(strList, ListK.foldable())
// Option.Some(3)
fun <F> reduceRightToOption(strKind: Kind<F, String>, FO: Foldable<F>): Option<Int> =
FO.run {
strKind.reduceRightToOption({ it.length }) { value: String, base: Eval<Int> -> base.map { it + value.length } }
.value()
}
reduceRightToOption(maybeStr, Option.foldable())
// Option.Some(3)
reduceRightToOption(None, Option.foldable())
// Option.None
reduceRightToOption(strList, ListK.foldable())
// Option.Some(3)
Reduce the elements of this structure down to a single value by applying the provided aggregation function in a left-associative manner.
Return None if the structure is empty, otherwise the result of combining the cumulative left-associative result of the f operation over all of the elements.
fun <F> getLengthFromLeft(strKind: Kind<F, String>, FO: Foldable<F>): Option<String> =
FO.run {
strKind.reduceLeftOption { base: String, value: String -> base + value }
}
getLengthFromLeft(maybeStr, Option.foldable())
// Option.Some(abc)
getLengthFromLeft(None, Option.foldable())
// Option.None
getLengthFromLeft(strList, ListK.foldable())
// Option.Some(abc)
Reduce the elements of this structure down to a single value by applying the provided aggregation function in a right-associative manner.
Return None if the structure is empty, otherwise the result of combining the cumulative right-associative result of the f operation over the A elements.
fun <F> getLengthFromRight(strKind: Kind<F, String>, FO: Foldable<F>): Option<String> =
FO.run {
strKind.reduceRightOption { value: String, base: Eval<String> -> base.map { it + value } }
.value()
}
getLengthFromRight(maybeStr, Option.foldable())
// Option.Some(abc)
getLengthFromRight(None, Option.foldable())
// Option.None
getLengthFromRight(strList, ListK.foldable())
// Option.Some(cba)
Fold implemented by mapping A
values into B
, and then combining them using the given Monoid<B>
instance.
fun <F> getLenght(strKind: Kind<F, String>, FO: Foldable<F>): Int =
FO.run {
strKind.foldMap(Int.monoid()) { it.length }
}
getLenght(maybeStr, Option.foldable())
// 3
getLenght(None, Option.foldable())
// 0
getLenght(strList, ListK.foldable())
// 3
A typed values will be mapped into Kind<G, B>
by function f
and combined using Applicative#map2
.
This method is primarily useful when <_>
represents an action or effect, and the specific A
aspect of Kind<G, A>
is
not otherwise needed.
import arrow.core.extensions.either.applicative.applicative
fun <F> traverse(strKind: Kind<F, String>, FO: Foldable<F>): Either<Int,Unit> =
FO.run {
strKind.traverse_(Either.applicative<Int>()) { Right(it.length) }
}.fix()
traverse(maybeStr, Option.foldable())
// Either.Right(kotlin.Unit)
traverse(None, Option.foldable())
// Either.Right(kotlin.Unit)
traverse(strList, ListK.foldable())
// Either.Right(kotlin.Unit)
Similar to traverse_
, except it operates on Kind<F, Kind<G, A>>
values, so no additional functions are needed.
import arrow.core.extensions.option.applicative.applicative
fun <F> sequence(strKind: Kind<F, Kind<ForOption, String>>, FO: Foldable<F>):Option<Unit> =
FO.run {
strKind.sequence_(Option.applicative())
}.fix()
val maybeStrOpt = Some("abc".some())
val strNoneList = listOf("a".some(), None, "c".some()).k()
val strOptList = listOf("a".some(), "b".some(), "c".some()).k()
sequence(maybeStrOpt, Option.foldable())
// Option.Some(kotlin.Unit)
sequence(None, Option.foldable())
// Option.Some(kotlin.Unit)
sequence(strNoneList, ListK.foldable())
// Option.None
sequence(strOptList, ListK.foldable())
// Option.Some(kotlin.Unit)
Find the first element matching the predicate, if one exists.
fun <F> getIfNotBlank(strKind: Kind<F, String>, FO: Foldable<F>): Option<String> =
FO.run {
strKind.find { it.isNotBlank() }
}
getIfNotBlank(maybeStr, Option.foldable())
// Option.Some(abc)
getIfNotBlank(None, Option.foldable())
// Option.None
getIfNotBlank(strList, ListK.foldable())
// Option.Some(a)
Check whether at least one element satisfies the predicate.
If there are no elements, the result is false.
fun <F> containsNotBlank(strKind: Kind<F, String>, FO: Foldable<F>): Boolean =
FO.run {
strKind.exists { it.isNotBlank() }
}
containsNotBlank(maybeStr, Option.foldable())
// true
containsNotBlank(None, Option.foldable())
// false
containsNotBlank(strList, ListK.foldable())
// true
Check whether all elements satisfy the predicate.
If there are no elements, the result is true.
fun <F> isNotBlank(strKind: Kind<F, String>, FO: Foldable<F>): Boolean =
FO.run {
strKind.forAll { it.isNotBlank() }
}
isNotBlank(maybeStr, Option.foldable())
// true
isNotBlank(None, Option.foldable())
// true
isNotBlank(strList, ListK.foldable())
// true
Returns true if there are no elements. Otherwise false.
fun <F> isFoldableEmpty(strKind: Kind<F, String>, FO: Foldable<F>): Boolean =
FO.run {
strKind.isEmpty()
}
isFoldableEmpty(maybeStr, Option.foldable())
// false
isFoldableEmpty(None, Option.foldable())
// true
isFoldableEmpty(strList, ListK.foldable())
// false
Returns true if there is at least one element. Otherwise false.
fun <F> foldableNonEmpty(strKind: Kind<F, String>, FO: Foldable<F>): Boolean =
FO.run {
strKind.nonEmpty()
}
foldableNonEmpty(maybeStr, Option.foldable())
// true
foldableNonEmpty(None, Option.foldable())
// false
foldableNonEmpty(strList, ListK.foldable())
// true
The size of this Foldable
.
Note: Will not terminate for infinite-sized collections.
fun <F> foldableSize(strKind: Kind<F, String>, FO: Foldable<F>): Long =
FO.run {
strKind.size(Long.monoid())
}
foldableSize(maybeStr, Option.foldable())
// 1
foldableSize(None, Option.foldable())
// 0
foldableSize(strList, ListK.foldable())
// 3
Monadic folding on F
by mapping A
values to Kind<G, B>
, combining the B
values using the given Monoid<B>
instance.
Similar to foldM
, but using a Monoid<B>
.
import arrow.core.extensions.option.monad.monad
fun <F> getLengthWithMonoid(strKind: Kind<F, String>, FO: Foldable<F>): Option<Int> =
FO.run {
strKind.foldMapM(Option.monad(), Int.monoid()) { Some(it.length) }
}.fix()
getLengthWithMonoid(maybeStr, Option.foldable())
// Option.Some(3)
getLengthWithMonoid(None, Option.foldable())
// Option.Some(0)
getLengthWithMonoid(strList, ListK.foldable())
// Option.Some(3)
Left associative monadic folding on F
.
The default implementation of this is based on foldL
, and thus will always fold across the entire structure.
Certain structures are able to implement this in such a way that folds can be short-circuited (not traverse the
entirety of the structure), depending on the G
result produced at a given step.
import arrow.core.extensions.either.monad.monad
fun <F> maybeConcatenateString(strKind: Kind<F, String>, FO: Foldable<F>): Either<String,String> =
FO.run {
strKind.foldM(
Either.monad<String>(),
"str: "
) { base: String, value: String -> Right(base + value) }
}.fix()
maybeConcatenateString(maybeStr, Option.foldable())
// Either.Right(str: abc)
maybeConcatenateString(None, Option.foldable())
// Either.Right(str: )
maybeConcatenateString(strList, ListK.foldable())
// Either.Right(str: abc)
Get the element at the index of the Foldable.
import arrow.core.extensions.either.monad.monad
import arrow.core.extensions.either.foldable.foldable
fun foldableGet(strKind: EitherOf<String, String>): Option<String> =
with(Either.foldable<String>()) {
strKind.get(0)
}
val rightStr = Either.right("abc") as Either<String, String>
foldableGet(rightStr)
// Option.Some(abc)
Do you like Arrow?
✖