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.