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 Eithers like Either[A, Either[B, C]]
- Examples of nested
Eithers
// 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