Either

Either<L, R> is a representation of one of two possible values L or R, but not both at the same time. Either is a sealed class (similar to Option) with two subtypes Left<L> and Right<R>. Usually Either is used to represent results that can fail, using the left side to represent the error and the right side to represent a successful result. Because representing operations that can fail is a common scenario, Arrow's Either is right biased, in other words, unless it is documented otherwise all operations run on the right side.

Let's translate our division example from Option to Either:

import arrow.core.Either
import arrow.core.Either.Right
import arrow.core.Either.Left

fun
eitherDivide(num: Int, den: Int): Either<String, Int> {
val option = optionDivide(num, den)
return when (option) {
is Some -> Right(option.t)
None -> Left("$num isn't divisible by $den")
}
}

Now instead of returning a None value, we're returning valuable information to our user:

import arrow.core.Tuple2

fun
eitherDivision(a: Int, b: Int, den: Int): Either<String, Tuple2<Int, Int>> {
val aDiv = eitherDivide(a, den)
return when (aDiv) {
is Right -> {
val bDiv = eitherDivide(b, den)
when (bDiv) {
is Right -> Right(aDiv.getOrElse { 0 } toT bDiv.getOrElse { 0 })
is Left -> bDiv as Either<String, Nothing>
}
}
is Left -> aDiv as Either<String, Nothing>
}
}

In eitherDivision, we're using Arrow's Tuple<A, B> instead of Kotlin's Pair<A, B>. Tuples provide more features than Pair/Triple, and from now on we'll use it. To create a Tuple2, you can use the extension infix function, toT.

Next, a short list of the Either<L, R> functions:

Function

Description

bimap(fa:(L) -> T, fb:(R) -> X): Either<T, X>

Transform using fa on Left and fb on Right to return Either<T, X>.

contains(elem:R): Boolean

Returns true if the Right value is the same as elem parameter, false for Left.

exists(p:Predicate<R>):Boolean

If Right, returns Predicate p result, always false for Left.

flatMap(f: (R) -> Either<L, T>): Either<L, T>

A flatMap function as in Monad, using the value of  Right.

fold(fa: (L) -> T, fb: (R) -> T): T

Returns a T value executing fa for Left and fb for Right.

getOrElse(default:(L) -> R): R

Returns Right value, or results from the default function.

isLeft(): Boolean

Returns true if is an instance of Left and false for Right.

isRight(): Boolean

Returns true if is an instance of Right and false for Left.

map(f: (R) -> T): Either<L, T>

A map function as in Functor, if Right, uses function f to transform it to Right<T>, if Left, returns same value without transformation.

mapLeft(f: (L) -> T): Either<T, R>

A map function as in Functor, if Left, uses function f to transform it to Left<T>, if Right, returns same value without transformation.

swap(): Either<R, L>

Returns Either with its types and value swapped.

toOption(): Option<R>

Some<T> for Right and None for Left.

The flatMap version looks as expected:

fun flatMapEitherDivision(a: Int, b: Int, den: Int): Either<String, Tuple2<Int, Int>> {
return eitherDivide(a, den).flatMap { aDiv ->
eitherDivide(b, den).flatMap { bDiv ->
Right(aDiv toT bDiv)
}
}
}

Either has a monad implementation, so we can invoke the binding function:

fun comprehensionEitherDivision(a: Int, b: Int, den: Int): Either<String, Tuple2<Int, Int>> {
return Either.monad<String>().binding {
val aDiv = eitherDivide(a, den).bind()
val bDiv = eitherDivide(b, den).bind()

aDiv toT bDiv
}.ev()

Pay attention to Either.monad<L>(); for Either<L, R> it must define the L type:

fun main(args: Array<String>) {
eitherDivision(3, 2, 4).fold(::println, ::println) //3 isn't divisible by 4
}

In our next section, we'll learn about monad transformers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.217.17