¿Por qué PartialFunction<: Function en Scala?
type-systems subtype (2)
Porque en Scala (como en cualquier lenguaje completo de Turing) no hay garantía de que una Función sea total.
val f = {x : Int => 1 / x}
Esa función no está definida en 0. Una función parcial es solo una función que promete decirle dónde no está definida. Aún así, Scala hace que sea lo suficientemente fácil hacer lo que quieras
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
Si lo prefiere, puede hacer func2Partial implícito.
En Scala, la PartialFunction[A, B]
se deriva del tipo Function[A, B]
(ver Scala Reference, 12.3.3). Sin embargo, esto me parece contradictorio, ya que una Function
(que debe definirse para todos los A
) tiene requisitos más estrictos que una función PartialFunction
, que puede estar indefinida en algunos lugares.
El problema que he encontrado es que cuando tengo una función parcial, no puedo usar una Function
para extender la función parcial. P.ej. No puedo hacerlo:
(pf orElse (_)=>"default")(x)
(Espero que la sintaxis sea al menos remotamente correcta)
¿Por qué esta subtipificación se realiza de forma inversa? ¿Hay alguna razón que he pasado por alto, como el hecho de que los tipos de Function
están incorporados?
Por cierto, también sería bueno si Function1 :> Function0
así que no necesito tener el argumento ficticio en el ejemplo anterior :-)
Editar para aclarar el problema de subtipado
La diferencia entre los dos enfoques se puede enfatizar mirando dos ejemplos. ¿Cuál de ellos es el correcto?
Uno:
val zeroOne : PartialFunction[Float, Float] = { case 0 => 1 }
val sinc = zeroOne orElse ((x) => sin(x)/x) // should this be a breach of promise?
Dos:
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?
PartialFunction
tiene métodos que Function1
no tiene, por lo tanto, es el subtipo. Esos métodos son isDefinedAt
and orElse
.
Su verdadero problema es que PartialFunction
s no se deduce a veces cuando realmente le gustaría que lo fueran. Tengo la esperanza de que se abordarán en una fecha futura. Por ejemplo, esto no funciona:
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" })
Pero esto hace:
scala> pf orElse ({ case x => "default" } : PartialFunction[String,String])
res5: PartialFunction[String,String] = <function>
Por supuesto, siempre puedes hacer esto:
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]
Y ahora es más como tú quieres:
scala> pf orElse ((x: String) => "default")
res7: PartialFunction[String,String] = <function>
scala> println(res7("a") + " " + res7("quux"))
foo default