Why is PartialFunction <: Function in Scala?

左心房为你撑大大i 提交于 2019-12-17 22:31:18

问题


In Scala, the PartialFunction[A, B] class is derived from type Function[A, B] (see Scala Reference, 12.3.3). However, this seems counterintuitive to me, since a Function (which needs to be defined for all A) has more stringent requirements than a PartialFunction, which can be undefined at some places.

The problem I've came accross was that when I have a partial function, I cannot use a Function to extend the partial function. Eg. I cannot do:

(pf orElse (_)=>"default")(x)

(Hope the syntax is at least remotely right)

Why is this subtyping done reversely? Are there any reasons that I've overlooked, like the fact that the Function types are built-in?

BTW, it would be also nice if Function1 :> Function0 so I needn't have the dummy argument in the example above :-)

Edit to clarify the subtyping problem

The difference between the two approaches can be emphasized by looking at two examples. Which of them is right?

One:

val zeroOne : PartialFunction[Float, Float] = { case 0 => 1 }
val sinc = zeroOne orElse ((x) => sin(x)/x) // should this be a breach of promise?

Two:

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?

回答1:


Because in Scala (as in any Turing complete language) there is no guarantee that a Function is total.

val f = {x : Int => 1 / x}

That function is not defined at 0. A PartialFunction is just a Function that promises to tell you where it's not defined. Still, Scala makes it easy enough to do what you want

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

If you prefer, you can make func2Partial implicit.




回答2:


PartialFunction has methods which Function1 does not, therefore it is the subtype. Those methods are isDefinedAt and orElse.

Your real problem is that PartialFunctions are not inferred sometimes when you'd really like them to be. I'm hopeful that will be addressed at some future date. For instance this doesn't work:

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" })

But this does:

scala> pf orElse ({ case x => "default" } : PartialFunction[String,String])
res5: PartialFunction[String,String] = <function>

Of course you could always do this:

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]

And now it's more like you want:

scala> pf orElse ((x: String) => "default")
res7: PartialFunction[String,String] = <function>

scala> println(res7("a") + " " + res7("quux"))
foo default


来源:https://stackoverflow.com/questions/930698/why-is-partialfunction-function-in-scala

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!