Пример 1. В чем причина возврата общей функции, если хотя бы одна из функций в композиции является частичной?
Это потому, что первая функция в вашем первом примере является общей функцией (Function1) и ее andThen возвращает функцию Function1 независимо от того, является ли вторая функция полной или частичной:
def andThen[A](g: (R) => A): (T1) => A
Я предполагаю, что команда разработчиков языка Scala предпочитает более обобщенное возвращаемое значение, поскольку PartialFunction является подклассом Function и позволяет пользователям создавать специализированный код по мере необходимости.
Пример 2: вызов calc.lift(0.5) вызовет MathError вместо возврата None
Из PartialFunction API doc, объединение двух частичных функций с помощью andThen
вернет частичную функцию с тем же доменом, что и первая частичная функция:
def andThen[C](k: (B) => C): PartialFunction[A, C]
Таким образом, результирующая составная функция игнорирует тот факт, что inverse(0.5)
(т. е. 2.0) находится за пределами домена второй частичной функции arcSin
.
Итак, при составлении функции (полной или частичной) с частичной функцией с использованием andThen
как мы можем заставить ее возвращать частичную функцию с правильным доменом?
Подобно тому, что продемонстрировано в этом SO Q&A, можно улучшить andThen
с помощью пары неявных классов, чтобы ограничить домен результирующей составленной функции в подмножество домена первой функции, которые возвращают значения в пределах домена частичной функции:
object ComposeFcnOps {
implicit class TotalCompose[A, B](f: Function[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => Option(f(x)).flatMap(that.lift))
}
implicit class PartialCompose[A, B](pf: PartialFunction[A, B]) {
def andThenPartial[C](that: PartialFunction[B, C]): PartialFunction[A, C] =
Function.unlift(x => pf.lift(x).flatMap(that.lift))
}
}
Тестирование с примерами функций:
import ComposeFcnOps._
val mod10: Int => Int = _ % 10
val inverse1: PartialFunction[Int, Double] = { case n if n != 0 => 1.0 / n }
val triple: Double => Double = _ * 3
val calc1 = mod10 andThenPartial inverse1 andThen triple
// calc1: PartialFunction[Int,Double] = <function1>
calc1.isDefinedAt(0)
// res1: Boolean = false
val inverse2: PartialFunction[Double, Double] = { case n if n != 0 => 1.0 / n }
val arcSin: PartialFunction[Double, Double] = {
case n if math.abs(n) <= 1 => math.asin(n)
}
val calc2 = inverse2 andThenPartial arcSin
// calc2: PartialFunction[Double,Double] = <function1>
calc2.isDefinedAt(0.5)
// res2: Boolean = false
calc2.lift(0.5)
// res3: Option[Double] = None
person
Leo C
schedule
19.03.2019