Почему PartialFunction <: Function в Scala?
В Скале PartialFunction[A, B]
класс является производным от типа Function[A, B]
(см. Scala Reference, 12.3.3). Тем не менее, это кажется мне нелогичным, так как Function
(который должен быть определен для всех A
) имеет более строгие требования, чем PartialFunction
, который может быть неопределенным в некоторых местах.
Проблема, с которой я столкнулся, заключалась в том, что, когда у меня есть частичная функция, я не могу использовать Function
расширить частичную функцию. Например. Я не могу сделать:
(pf orElse (_)=>"default")(x)
(Надеюсь, синтаксис хотя бы удаленно правильный)
Почему этот подтип выполняется наоборот? Есть ли какие-то причины, которые я упустил из виду, например, тот факт, что Function
типы встроены?
Кстати, было бы также хорошо, если Function1 :> Function0
поэтому мне не нужно иметь фиктивный аргумент в приведенном выше примере:-)
Изменить, чтобы уточнить проблему с подтипами
Различие между этими двумя подходами можно подчеркнуть, рассмотрев два примера. Кто из них прав?
Один:
val zeroOne : PartialFunction[Float, Float] = { case 0 => 1 }
val sinc = zeroOne orElse ((x) => sin(x)/x) // should this be a breach of promise?
Два:
def foo(f : (Int)=>Int) {
print(f(1))
}
val bar = new PartialFunction[Int, Int] {
def apply(x : Int) = x/2
def isDefinedAt(x : Int) = x%2 == 0
}
foo(bar) // should this be a breach of promise?
2 ответа
Потому что в Scala (как и в любом полном языке Тьюринга) нет гарантии, что функция является полной.
val f = {x : Int => 1 / x}
Эта функция не определена в 0. PartialFunction - это просто функция, которая обещает сообщить вам, где она не определена. Тем не менее, Scala позволяет достаточно легко делать то, что вы хотите
def func2Partial[A,R](f : A => R) : PartialFunction[A,R] = {case x => f(x)}
val pf : PartialFunction[Int, String] = {case 1 => "one"}
val g = pf orElse func2Partial{_ : Int => "default"}
scala> g(1)
res0: String = one
scala> g(2)
res1: String = default
Если вы предпочитаете, вы можете сделать func2Partial неявным.
PartialFunction
есть методы, которые Function1
нет, поэтому это подтип. Эти методы isDefinedAt
а также orElse
,
Ваша настоящая проблема в том, что PartialFunction
s не выводятся иногда, когда вы действительно хотели бы, чтобы они были. Я надеюсь, что это будет рассмотрено в будущем. Например, это не работает:
scala> val pf: PartialFunction[String, String] = { case "a" => "foo" }
pf: PartialFunction[String,String] = <function>
scala> pf orElse { case x => "default" }
<console>:6: error: missing parameter type for expanded function
((x0$1) => x0$1 match { case (x @ _) => "default" })
Но это делает:
scala> pf orElse ({ case x => "default" } : PartialFunction[String,String])
res5: PartialFunction[String,String] = <function>
Конечно, вы всегда можете сделать это:
scala> implicit def f2pf[T,R](f: Function1[T,R]): PartialFunction[T,R] =
new PartialFunction[T,R] {
def apply(x: T) = f(x)
def isDefinedAt(x: T) = true
}
f2pf: [T,R](f: (T) => R)PartialFunction[T,R]
А теперь это больше похоже на то, что вы хотите:
scala> pf orElse ((x: String) => "default")
res7: PartialFunction[String,String] = <function>
scala> println(res7("a") + " " + res7("quux"))
foo default