为什么PartialFunction <:Scala中的函数?

jpa*_*cek 44 type-systems scala subtype

在Scala中,PartialFunction[A, B]类派生自类型Function[A, B](请参阅Scala参考,12.3.3).然而,这对我来说似乎违反直觉,因为a Function(需要为所有人定义A)具有比a更严格的要求PartialFunction,这在某些地方可能是不确定的.

我遇到的问题是,当我有部分功能时,我不能使用a Function来扩展部分功能.例如.我不能做:

(pf orElse (_)=>"default")(x)
Run Code Online (Sandbox Code Playgroud)

(希望语法至少是远程正确的)

为什么这个子类型反向完成?是否有任何我忽略的原因,比如这些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?
Run Code Online (Sandbox Code Playgroud)

二:

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?
Run Code Online (Sandbox Code Playgroud)

Jam*_*Iry 34

因为在Scala中(如在任何图灵完整语言中),不能保证函数是完全的.

val f = {x : Int => 1 / x}
Run Code Online (Sandbox Code Playgroud)

该函数未定义为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
Run Code Online (Sandbox Code Playgroud)

如果您愿意,可以隐式使用func2Partial.

  • Scala不能强迫你不要在你的isDefinedAt方法中撒谎.所以Scala认为你的PartialFunction"有效"但是用户会因为你没有"正确地"做到这一点而感到烦恼.scala> val pf:PartialFunction [Any,Int] = {case x:Int if x!= 0 => 1/x} pf:PartialFunction [Any,Int] = <function> scala> pf.isDefinedAt("hello") res2:Boolean = false scala> pf.isDefinedAt(0)res3:Boolean = false scala> pf.isDefinedAt(1)res4:Boolean = true (7认同)
  • 在CS中,任何可以为某些输入抛出异常的函数都是部分*.甚至对于你的函数来说,这应该是将异常转换为某些东西,而是抛出另一个异常.这不是一件坏事.这可能是正确的选择.在这一点上,我得到的印象是你不希望你的原始问题得到解答 - 你只想辩论设计选择.团队会听取访问scala辩论邮件列表的信息.*作为一般规则,像OOM这样的事情可以在任何地方和任何时间发生,这种讨论都会被忽略. (6认同)
  • 正式地说,如果域包含0,则f(x)= 1/x是部分函数.由于Scala的类型系统不允许你说"Int除0之外",那么我写的是部分函数.它不是PartialFunction对象,因为它没有一个方法来告诉调用者它在哪里而且没有定义. (4认同)
  • 如果PartialFunction真的答应告诉它未定义的地方,那么这一点是有效的.但是,我不相信这是真的.Reference表示PartialFunction在某些点上是一个未定义的函数.我不认为这意味着像{case x:Int => 1/x}这样的函数是无效的(因为它无法履行承诺),只是异常是它的预期行为. (2认同)

psp*_*psp 17

PartialFunctionFunction1没有的方法,因此它是子类型.那些方法是isDefinedAtorElse.

你真正的问题是,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" })
Run Code Online (Sandbox Code Playgroud)

但这样做:

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

当然你总能做到这一点:

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]
Run Code Online (Sandbox Code Playgroud)

现在它更像你想要的:

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

scala> println(res7("a") + " " + res7("quux"))
foo default
Run Code Online (Sandbox Code Playgroud)

  • 我并不反对它可以在任何一个方向上建模,但我真的没有看到为什么你认为一个方向明显比另一方向更明智.在实践中,函数比部分函数多很多倍,并且因为它们都是特征,所以代码在使用它们的每个类中都是重复的.所以也有工程原因PF应该是F1的专业化而不是相反.但如果它们更容易互换则会很好. (7认同)