¿Por qué es función parcial <: Función en Scala?
-
06-09-2019 - |
Pregunta
En Scala, la clase PartialFunction[A, B]
se deriva de tipo Function[A, B]
(ver Scala Reference, 12.3.3). Sin embargo, esto parece contrario a la intuición para mí, ya que un Function
(que debe ser definido para todos A
) tiene requisitos más estrictos que un PartialFunction
, que puede ser indefinido en algunos lugares.
El problema que he encontré con que era cuando tengo una función parcial, no puedo utilizar un Function
para ampliar la función parcial. P.ej. No puedo hacer:
(pf orElse (_)=>"default")(x)
(Hope la sintaxis es al menos de forma remota derecha)
¿Por qué se hace en sentido inverso subtipos? ¿Hay algunas razones por las que he pasado por alto, al igual que el hecho de que los tipos Function
están incorporados?
Por cierto, sería bueno si también Function1 :> Function0
por lo que no necesita tener el argumento ficticio en el ejemplo anterior: -)
Editar para aclarar el problema de subtipos
La diferencia entre los dos enfoques se pueden valorizar con dos ejemplos. ¿Cuál de ellas es la correcta?
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?
No hay solución correcta
Otros consejos
Debido a que en Scala (como en cualquier lenguaje Turing completo) no hay garantía de que una función es total.
val f = {x : Int => 1 / x}
Esta función no está definida en 0. Una función parcial es sólo una función que promete decir que donde no está definido. Aún así, Scala hace que sea bastante fácil de hacer lo que quiera
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ícita.
PartialFunction
tiene métodos que Function1
no, por lo tanto, es el subtipo. Esos métodos son isDefinedAt
y orElse
.
Su verdadero problema es que PartialFunction
s no se infieren a veces, cuando realmente te gustaría que fueran. Tengo la esperanza de que será tratado 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" })
Sin embargo, esto hace:
scala> pf orElse ({ case x => "default" } : PartialFunction[String,String])
res5: PartialFunction[String,String] = <function>
Por supuesto, siempre se puede 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 desea:
scala> pf orElse ((x: String) => "default")
res7: PartialFunction[String,String] = <function>
scala> println(res7("a") + " " + res7("quux"))
foo default