How to combine Option values in Scala?
Solution 1
scala> val (x, y) = (Some(4), Some(9))
x: Some[Int] = Some(4)
y: Some[Int] = Some(9)
scala> def f(x: Int, y: Int) = Math.max(x, y)
f: (x: Int,y: Int)Int
scala> for { x0 <- x; y0 <- y } yield f(x0, y0)
res26: Option[Int] = Some(9)
scala> val x = None
x: None.type = None
scala> for { x0 <- x; y0 <- y } yield f(x0, y0)
res27: Option[Int] = None
Solution 2
@RahulG's answer exploits the fact that Option
is a monad (even though there is no type to represent this in the Scala library). The compiler expands the for
comprehension to the following:
def a: Option[Int]
def b: Option[Int]
val calc: Option[Int] = a flatMap {aa => b map {bb => aa + bb}}
You can also treat it as an applicative functor, with some help from Scalaz:
import scalaz._
import Scalaz._
def a: Option[Int]
def b: Option[Int]
val calc: Option[Int] = (a ⊛ b) {_ + _}
A key difference is that in the monadic calculation, a failure (that is, None
) of calculation a
short circuits the evaluation. In the applicative style, both a
and b
are evaluated, and if both are Some
s, the pure function is called. You can also see that in the monadic calculation, the value aa
could have been used in the calculation b
; in the applicative version, b
cannot depend on the result of a
.
Solution 3
I have a slightly older version of scalaz than retronym but the following works for me as an example and is generalizable for the case where you have 3 types T, U, V
and not just one:
def main(args: Array[String]) {
import scalaz._
import Scalaz._
val opt1 = some(4.0) //Option[Double]
val opt2 = some(3) //Option[Int]
val f: (Double, Int) => String = (d, i) => "[%d and %.2f]".format(i, d)
val res = (opt1 <|*|> opt2).map(f.tupled)
println(res) //Some([3 and 4.00])
}
I can then add:
val opt3 = none[Int]
val res2 = (opt1 <|*|> opt3).map(f.tupled)
println(res2) //None
Solution 4
Starting Scala 2.13
, Option#zip
can be applied to another Option
to return an Option
(in earlier versions, it would have returned an Iterable
); thus:
def optApply[T](f: (T,T) => T, a: Option[T], b: Option[T]): Option[T] =
a.zip(b).map(f.tupled)
where the behavior of zip
is:
Some(2).zip(Some(3)) // Some((2, 3))
Some(2).zip(None) // None
None.zip(Some(3)) // None
None.zip(None) // None
and which can be applied as such:
optApply[Int]((a, b) => a max b, Some(2), Some(5)) // Some(5)
optApply[Int]((a, b) => a max b, Some(2), None) // None
Comments
-
Jeff about 2 years
I want to be able to apply an operation
f: (T,T) => T
toOption[T]
values in Scala. I want the result to beNone
if any of the two values isNone
.More specifically, I want to know if there is a shorter way to do the following:
def opt_apply[T](f: (T,T) => T, x: Option[T], y: Option[T]): Option[T] = { (x,y) match { case (Some(u),Some(v)) => Some(f(u,v)) case _ => None } }
I have tryied
(x zip y) map {case (u,v) => f(u,v)}
but the result is anIterator[T]
not anOption[T]
. -
oxbow_lakes about 14 yearsIs the ASCII equivalent
<|*|>
for this method? -
retronym about 14 years
<*>
allows you to provide the 'pure' function, in this case(a, b)=> a + b
.<|*|>
is a convenience for usingTuple2.apply
as the pure function.⊛
is actually a bit more general than arity-2, you could write(a ⊛ b a ⊛ b) {_ + _ + _ + _}
. It's a little experimental, and as such, doesn't have a ASCII alias yet. -
retronym about 14 yearsTypo, I meant:
(a ⊛ b ⊛ a ⊛ b) {_ + _ + _ + _}
-
user142435 about 14 yearsThat's weird. I didn't see @RahulG's answer when I posted this.
-
retronym about 14 yearsReplace
<|*|>
with<*>
to avoid creation of the temporary tuple, and usef
directly. -
Seigert over 10 yearsFrom the 7th version of Scalaz ASCII alias to
⊛
operator is|@|
:val calc: Option[Int] = (a |@| b) {_ + _}