Reputation: 31576
When I am coding with options I find the fold method very useful. Instead of writing if defined statements I can do
opt.fold(<not_defined>){ defined => }
this is good. but what to do if we are working with multiple options. or multiple eithers. Now I have to resort to writing code like
if (x.isDefined && y.isRight) {
val z = getSomething(x.get)
if (z.isDefined) {
....
Depending on the number of things involved, this code becomes very nested.
is there a functional trick to make this code a little un-nested and concise.... like the fold operation above?
Upvotes: 1
Views: 263
Reputation: 379
Cases when .isDefined
is followed by .get
call can be refactored using custom extractors for pattern matching:
def getSomething(s: String): Option[String] = if (s.isEmpty) None else Some(s.toUpperCase)
object MyExtractor {
def unapply(t: (Option[String], Either[Int, String])): Option[String] =
t match {
case (Some(x), Right(y)) => getSomething(x)
case _ => None
}
}
val x: Option[String] = Some("hello world")
val y: Either[Int, String] = Right("ok")
(x, y) match {
case MyExtractor(z) => z // let's do something with z
case _ => "world"
}
// HELLO WORLD
We managed to get rid of all .isDefined
, .get
and even .right
calls by replacing them by explicit pattern matching thanks to our custom extractor MyExtractor
.
Upvotes: 1
Reputation: 7865
have you tried for comprehension? Assuming you don't want to treat individual errors or empty optionals:
import scala.util._
val opt1 = Some("opt1")
val either2: Either[Error, String] = Right("either2")
val try3: Try[String] = Success("try3")
for {
v1 <- opt1
v2 <- either2.right.toOption
v3 <- try3.toOption
} yield {
println(s"$v1 $v2 $v3")
}
Note that Either
is not right biased, so you need to call the .right
method on the for comprehension (I think cats or scalaz have a right biased Either). Also, we are converting the Either
and the Try
to optionals, discarding errors
Upvotes: 1