Another possibility:
def avg(left: Option[Double], right: Option[Double])(default: => Double): Double =
left.flatMap(a => right.map(b => (a + b) / 2))
.orElse(left)
.orElse(right)
.getOrElse(default)
You flatMap
over the left option: if it's not empty, you take the right option and map
its content and average with the content of the left option. If either option is empty, the result is None
, so you can defined either left
or right
as fallback values with orElse
. Finally, the result is retrieved with getOrElse
and if both inputs where empty, the default
is returned.
You can adapt this to adopt any behavior. To make a function that throws if both options are empty you can do the following:
val assertAvg = avg(_ : Option[Double], _ : Option[Double])(sys.error("both operands are empty"))
This works because the type of throw
expressions is Nothing
, which is a subtype of any other type (including Double
), i.e. it can be returned as a result of any expression, regardless the expected type.
The code (and some tests) are available here on Scastie.