Either
note
Also check out: Either
Construct Either
Conditionally create either Right
or Left
.
val num1 = 1
// num1: Int = 1
Either.cond(num1 > 0, num1, "It should be a positive number")
// res0: Either[String, Int] = Right(value = 1)
val num2 = 0
// num2: Int = 0
Either.cond(num2 > 0, num2, "It should be a positive number")
// res1: Either[String, Int] = Left(value = "It should be a positive number")
val num3 = -1
// num3: Int = -1
Either.cond(num3 > 0, num3, "It should be a positive number")
// res2: Either[String, Int] = Left(value = "It should be a positive number")
Change Value
map
Either.map
is used to change the value inside Either
when it's Right
as Either
is right-biased.
info
NOTE: Each map
returns a new Either
instance.
// When value: B, value.asRight[A] to create Either[A, B] = Right(value)
// When value2: A, value2.asLeft[B] to create Either[A, B] = Left(value2)
import cats.syntax.all._
val number = 1.asRight[String]
// number: Either[String, Int] = Right(value = 1)
number.map(n => n * 2)
// res4: Either[String, Int] = Right(value = 2)
number.map(n => n + 10)
// res5: Either[String, Int] = Right(value = 11)
number.map(n => n - 1)
// res6: Either[String, Int] = Right(value = 0)
val number2 = "Error".asLeft[Int]
// number2: Either[String, Int] = Left(value = "Error")
number2.map(n => n * 2)
// res7: Either[String, Int] = Left(value = "Error")
number2.map(n => n + 10)
// res8: Either[String, Int] = Left(value = "Error")
number2.map(n => n - 1)
// res9: Either[String, Int] = Left(value = "Error")
import cats.syntax.all._
val number = 1.asRight[String]
// number: Either[String, Int] = Right(value = 1)
number.map(n => s"Number is ${n.toString}")
// res11: Either[String, String] = Right(value = "Number is 1")
val number2 = "Error".asLeft[Int]
// number2: Either[String, Int] = Left(value = "Error")
number2.map(n => s"Number is ${n.toString}")
// res12: Either[String, String] = Left(value = "Error")
flatMap
If the return type of the function passed to Either.map
is also Either
, use Either.flatMap
.
Otherwise, you will end up having nested Either
s like Either[A, Either[B, C]]
- Examples of nested
Either
s
// When value: B, value.asRight[A] to create Either[A, B] = Right(value)
// When value2: A, value2.asLeft[B] to create Either[A, B] = Left(value2)
import cats.syntax.all._
val number = 1.asRight[String]
// number: Either[String, Int] = Right(value = 1)
number.map(n => (n * 2).asRight)
// res14: Either[String, Either[Nothing, Int]] = Right(
// value = Right(value = 2)
// )
number.map(n => (n + 10).asRight)
// res15: Either[String, Either[Nothing, Int]] = Right(
// value = Right(value = 11)
// )
number.map(n => (n - 1).asRight)
// res16: Either[String, Either[Nothing, Int]] = Right(
// value = Right(value = 0)
// )
val number2 = "Error".asLeft[Int]
// number2: Either[String, Int] = Left(value = "Error")
number2.map(n => (n * 2).asRight)
// res17: Either[String, Either[Nothing, Int]] = Left(value = "Error")
number2.map(n => (n + 10).asRight)
// res18: Either[String, Either[Nothing, Int]] = Left(value = "Error")
number2.map(n => (n - 1).asRight)
// res19: Either[String, Either[Nothing, Int]] = Left(value = "Error")
- With
flatMap
// When value: B, value.asRight[A] to create Either[A, B] = Right(value)
// When value2: A, value2.asLeft[B] to create Either[A, B] = Left(value2)
import cats.syntax.all._
val number = 1.asRight
// number: Either[Nothing, Int] = Right(value = 1)
number.flatMap(n => (n * 2).asRight)
// res21: Either[Nothing, Int] = Right(value = 2)
number.flatMap(n => (n + 10).asRight)
// res22: Either[Nothing, Int] = Right(value = 11)
number.flatMap(n => (n - 1).asRight)
// res23: Either[Nothing, Int] = Right(value = 0)
val number2 = "Error".asLeft[Int]
// number2: Either[String, Int] = Left(value = "Error")
number2.flatMap(n => (n * 2).asRight)
// res24: Either[String, Int] = Left(value = "Error")
number2.flatMap(n => (n + 10).asRight)
// res25: Either[String, Int] = Left(value = "Error")
number2.flatMap(n => (n - 1).asRight)
// res26: Either[String, Int] = Left(value = "Error")
- More examples
// When value: B, value.asRight[A] to create Either[A, B] = Right(value)
// When value2: A, value2.asLeft[B] to create Either[A, B] = Left(value2)
import cats.syntax.all._
val number = 1.asRight
// number: Either[Nothing, Int] = Right(value = 1)
number.flatMap { n =>
if (n < 0) "n should not be a negative Int".asLeft
else (n * 2).asRight
}
// res28: Either[String, Int] = Right(value = 2)
val number2 = -1.asRight
// number2: Either[Nothing, Int] = Right(value = -1)
number2.flatMap { n =>
if (n < 0) "n should not be a negative Int".asLeft
else (n * 2).asRight
}
// res29: Either[String, Int] = Left(value = "n should not be a negative Int")
val number3 = "Error".asLeft[Int]
// number3: Either[String, Int] = Left(value = "Error")
number3.flatMap { n =>
if (n < 0) "n should not be a negative Int".asLeft
else (n * 2).asRight
}
// res30: Either[String, Int] = Left(value = "Error")
Get Value
There are few ways to get the value out of Either
. Since Either
can be either Right
or Left
, you need to provide ways to handle both Right
and Left
values.
Pattern Matching
import cats.syntax.all._
val errorOrNumber1 = 999.asRight[String]
// errorOrNumber1: Either[String, Int] = Right(value = 999)
errorOrNumber1 match {
case Right(n) => n
case Left(error) => 0
}
// res32: Int = 999
val errorOrNumber2 = "ERROR".asLeft[Int]
// errorOrNumber2: Either[String, Int] = Left(value = "ERROR")
errorOrNumber2 match {
case Right(n) => n
case Left(error) => 0
}
// res33: Int = 0
fold
(Catamorphism)
import cats.syntax.all._
val errorOrNumber1 = 999.asRight[String]
// errorOrNumber1: Either[String, Int] = Right(value = 999)
errorOrNumber1.fold(
error => 0,
n => n
)
// res35: Int = 999
val errorOrNumber2 = "ERROR".asLeft[Int]
// errorOrNumber2: Either[String, Int] = Left(value = "ERROR")
errorOrNumber2.fold(
error => 0,
n => n
)
// res36: Int = 0
val errorOrNumber3 = 999.asRight[String]
// errorOrNumber3: Either[String, Int] = Right(value = 999)
errorOrNumber3.fold(
error => 0,
n => n + 1
)
// res37: Int = 1000
val errorOrNumber4 = "ERROR".asLeft[Int]
// errorOrNumber4: Either[String, Int] = Left(value = "ERROR")
errorOrNumber4.fold(
error => 0,
n => n + 1
)
// res38: Int = 0
val errorOrNumber5 = 999.asRight[String]
// errorOrNumber5: Either[String, Int] = Right(value = 999)
errorOrNumber5.fold(
error => println(s"ERROR: $error"),
n => println(s"Number=$n")
)
// Number=999
val errorOrNumber6 = "ERROR".asLeft[Int]
// errorOrNumber6: Either[String, Int] = Left(value = "ERROR")
errorOrNumber6.fold(
error => println(s"ERROR: $error"),
n => println(s"Number=$n")
)
// ERROR: ERROR
getOrElse
import cats.syntax.all._
val errorOrNumber1 = 999.asRight[String]
// errorOrNumber1: Either[String, Int] = Right(value = 999)
errorOrNumber1.getOrElse(0)
// res42: Int = 999
val errorOrNumber2 = "ERROR".asLeft[Int]
// errorOrNumber2: Either[String, Int] = Left(value = "ERROR")
errorOrNumber2.getOrElse(0)
// res43: Int = 0
Check and Search
filterOrElse
note
Either
is Right
-biased so filterOrElse
works only for Right
. When it's Left
nothing happens with filterOrElse
.
import cats.syntax.all._
val errorOrNumber1 = 1.asRight[String]
// errorOrNumber1: Either[String, Int] = Right(value = 1)
errorOrNumber1.filterOrElse(n => n >= 0, -999)
// res45: Either[Any, Int] = Right(value = 1)
errorOrNumber1.filterOrElse(n => n >= 1, -999)
// res46: Either[Any, Int] = Right(value = 1)
errorOrNumber1.filterOrElse(n => n > 1, -999)
// res47: Either[Any, Int] = Left(value = -999)
val errorOrNumber2 = "No number found".asLeft[Int]
// errorOrNumber2: Either[String, Int] = Left(value = "No number found")
errorOrNumber2.filterOrElse(n => n >= 0, -999)
// res48: Either[Any, Int] = Left(value = "No number found")
errorOrNumber2.filterOrElse(n => n >= 1, -999)
// res49: Either[Any, Int] = Left(value = "No number found")
errorOrNumber2.filterOrElse(n => n > 1, -999)
// res50: Either[Any, Int] = Left(value = "No number found")
exists
import cats.syntax.all._
val errorOrNumber1 = 1.asRight
// errorOrNumber1: Either[Nothing, Int] = Right(value = 1)
errorOrNumber1.exists(n => n >= 0)
// res52: Boolean = true
errorOrNumber1.exists(n => n >= 1)
// res53: Boolean = true
errorOrNumber1.exists(n => n > 1)
// res54: Boolean = false
val errorOrNumber2 = "No number found".asLeft[Int]
// errorOrNumber2: Either[String, Int] = Left(value = "No number found")
errorOrNumber2.exists(n => n >= 0)
// res55: Boolean = false
errorOrNumber2.exists(n => n >= 1)
// res56: Boolean = false
errorOrNumber2.exists(n => n > 1)
// res57: Boolean = false