>我之前问过这个问题:将 Partial Function 与常规函数相结合
然后意识到,我实际上并没有问对。所以,这是另一个尝试。
如果我这样做:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar = foo orElse { case x => x.toString }
它不编译:error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: PartialFunction[?,?]
但这工作正常:
val x: Seq[String] = List(1,2,3).collect { case x => x.toString }
问题是有什么区别?在这两种情况下,参数的类型是相同的:PartialFunction[Int, String]
。传入的值实际上是相同的。为什么一种情况有效,而另一种情况无效?
您需要指定bar
的类型,因为编译器无法推断它。这将编译:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar : (Int => String) = foo orElse { case x => x.toString }
在List(1,2,3).collect{case x => x.toString}
的情况下,编译器能够根据List
的类型推断分部函数的输入类型。
final override def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[List[A], B, That])
根据类型参数,编译器可以推断您正在传递正确类型的分部函数。这就是为什么List(1,2,3).collect{case x:String => x.toString}
不编译,也不List(1,2,3).collect{case x:Int => x.toString; case x: String => x.toString}
.
由于List
是协变的,编译器能够推断偏函数{case x => x.toString}
是Int
上的偏函数。您会注意到List(1,2,3).collect{case x => x.length}
无法编译,因为编译器推断您正在Int
的Int
或子类上运行。
还要记住,{case x => x.toString}
只是句法糖。如果我们执行以下操作,那么您的示例将按预期工作
val f = new PartialFunction[Int, String](){
override def isDefinedAt(x: Int): Boolean = true
override def apply(v1: Int): String = v1.toString
}
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar = foo orElse f //This compiles fine.
List(1,2,3).collect{f} // This works as well.
因此,从我的角度来看,唯一合乎逻辑的答案是,能够为{case x => x.toString}
生成PartialFunction
实例的语法糖在编译时没有足够的信息,无法在orElse
情况下将其充分键入为PartialFunction[Int, String]
。
您可以使用库 Extractor.scala。
import com.thoughtworks.Extractor._
// Define a PartialFunction
val pf: PartialFunction[Int, String] = {
case 1 => "matched by PartialFunction"
}
// Define an optional function
val f: Int => Option[String] = { i =>
if (i == 2) {
Some("matched by optional function")
} else {
None
}
}
// Convert an optional function to a PartialFunction
val pf2: PartialFunction[Int, String] = f.unlift
util.Random.nextInt(4) match {
case pf.extract(m) => // Convert a PartialFunction to a pattern
println(m)
case f.extract(m) => // Convert an optional function to a pattern
println(m)
case pf2.extract(m) => // Convert a PartialFunction to a pattern
throw new AssertionError("This case should never occur because it has the same condition as `f.extract`.")
case _ =>
println("Not matched")
}