【问题标题】:Why is PartialFunction <: Function in Scala?为什么 PartialFunction <: Scala 中的函数?
【发布时间】:2009-05-30 21:51:01
【问题描述】:

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

我遇到的问题是,当我有一个偏函数时,我不能使用Function 来扩展偏函数。例如。我做不到:

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

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

为什么这个子类型是相反的?是否有任何我忽略的原因,例如 Function 类型是内置的?

顺便说一句,如果Function1 :&gt; Function0 也很好,所以我不需要上面示例中的虚拟参数:-)

编辑以澄清子类型问题

通过查看两个示例可以强调两种方法之间的区别。哪一个是对的?

一个:

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

两个:

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?

【问题讨论】:

  • 这个问题是否已经回答或仍然开放?
  • 我不知道。我正在考虑关闭它,因为我认为它会在一些邮件列表或其他东西中更快乐......

标签: scala type-systems subtype


【解决方案1】:

因为在 Scala 中(就像在任何图灵完备语言中一样),不能保证函数是完全的。

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

该函数未定义为 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

如果您愿意,可以将 func2Partial 设为隐式。

【讨论】:

  • 如果 PartialFunction 真的承诺告诉它在哪里未定义,那么这一点将是有效的。但是,我不相信这是真的。参考资料说 PartialFunction 在某些时候是未定义的函数。我不认为这意味着像 { case x:Int => 1/x } 这样的函数是无效的(因为它没有履行承诺),只是例外是它的预期行为。
  • 正式来说 f(x) = 1/x 是一个偏函数,如果域包含 0。由于 Scala 的类型系统不允许你说“除 0 之外的 Int”,那么我写的是偏函数功能。它只是不是 PartialFunction 对象,因为它没有方法来告诉调用者它在哪里以及未定义。
  • Scala 不能强迫您不要使用 isDefinedAt 方法。因此 Scala 认为您的 PartialFunction “有效”,但用户会因为您没有“正确”执行而感到恼火。 scala> val pf : PartialFunction[Any,Int] = {case x : Int if x != 0 => 1/x} pf: PartialFunction[Any,Int] = scala> pf.isDefinedAt("hello") res2: Boolean = false scala> pf.isDefinedAt(0) res3: Boolean = false scala> pf.isDefinedAt(1) res4: Boolean = true
  • 您一直在告诉我“谎言”,但我仍然看不出 Scala 规范在哪里说 PartialFunction 在 isDefinedAt() 的某个点上抛出异常是在说谎。例如,在关于 try-expressions 的段落中,参考说“......处理程序应符合类型 PartialFunction[Throwable, pt] ...”所以,类似于“try something catch { case x:SomeException => throw OtherException }" 无效,因为处理程序从其域中抛出一个给定点的异常?或者,在什么情况下,函数“定义”得足够多以至于它不是“说谎”?
  • 在 CS 中,任何可以为某些输入引发异常的函数都是部分函数*。这甚至适用于您的函数,该函数应该将异常转换为某种东西,而是抛出另一个异常。这并不是一件坏事。这可能是正确的选择。在这一点上,我的印象是你不想回答你原来的问题——你只是想辩论设计选择。请访问 scala-debate 邮件列表,团队会倾听。 * 作为一般规则,这种讨论会忽略任何地方和任何时间可能发生的 OOM。
【解决方案2】:

PartialFunction 具有 Function1 没有的方法,因此它是子类型。这些方法是isDefinedAtorElse

您真正的问题是,PartialFunctions 有时在您真正希望的情况下不会被推断出来。我希望这将在未来的某个日期得到解决。例如这不起作用:

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

但这确实:

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

当然,你总是可以这样做:

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]

现在它更像你想要的:

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

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

【讨论】:

  • 对不起,但我不买“PartialFunction 有 Function1 没有的方法,因此它是子类型”参数。这就像说如果你有一个类 EvenInteger 有加法、乘法和相等,和一个类 Integer 有加法、乘法、相等和附加方法 isEven(都是不可变的),附加方法使 Integer <: eveninteger>
  • 我不反对它可以在任一方向上建模,但我真的不明白为什么您认为一个方向显然比另一个方向更明智。在实践中,函数比部分函数更常见很多倍,并且由于它们都是特征,因此代码在每个使用它们的类中都重复。因此,PF 应该是 F1 的专业化而不是相反,也有工程上的原因。但如果它们更容易互换就更好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多