Perché funzione parziale <: funzione in Scala?
-
06-09-2019 - |
Domanda
In Scala, la classe PartialFunction[A, B]
è derivato dal tipo Function[A, B]
(vedi Scala di riferimento, 12.3.3). Tuttavia, questo sembra controintuitivo per me, dal momento che un Function
(che deve essere definita per tutto A
) ha requisiti più severi di un PartialFunction
, che può essere definita per alcuni posti.
Il problema che ho mai incontrato era che quando ho una funzione parziale, non posso usare un Function
per estendere la funzione parziale. Per esempio. Non posso fare:
(pf orElse (_)=>"default")(x)
(La speranza la sintassi è almeno in remoto destra)
Perché questo sottotipo fatto in senso inverso? Ci sono ragioni che ho trascurato, come il fatto che i tipi di Function
sono incorporati?
A proposito, sarebbe anche bello se Function1 :> Function0
quindi non è soggetta all'obbligo dei argomento fittizio nell'esempio di cui sopra: -)
Modifica per chiarire il problema di sottotipo
La differenza tra i due approcci può essere sottolineato, cercando in due esempi. Quale di loro è giusto?
Uno:
val zeroOne : PartialFunction[Float, Float] = { case 0 => 1 }
val sinc = zeroOne orElse ((x) => sin(x)/x) // should this be a breach of promise?
Due:
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?
Nessuna soluzione corretta
Altri suggerimenti
A causa a Scala (come in qualsiasi lingua completa Turing) non v'è alcuna garanzia che una funzione è totale.
val f = {x : Int => 1 / x}
Questa funzione non è definito a 0. Una funzione parziale è solo una funzione che promette di dirvi dove non è definito. Eppure, Scala rende facile abbastanza per fare quello che vuoi
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
Se si preferisce, si può fare func2Partial implicita.
PartialFunction
ha metodi che Function1
non, quindi è il sottotipo. Tali metodi sono isDefinedAt
e orElse
.
Il vero problema è che PartialFunction
s non vengono dedotte a volte quando vuoi davvero che siano. Sono fiducioso che verrà affrontato in un prossimo futuro. Per esempio questo non funziona:
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" })
Ma questo fa:
scala> pf orElse ({ case x => "default" } : PartialFunction[String,String])
res5: PartialFunction[String,String] = <function>
Naturalmente si può sempre fare questo:
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]
E ora è più come si desidera:
scala> pf orElse ((x: String) => "default")
res7: PartialFunction[String,String] = <function>
scala> println(res7("a") + " " + res7("quux"))
foo default