问题
I asked this question earlier: Combine a PartialFunction with a regular function
and then realized, that I haven't actually asked it right. So, here goes another attempt.
If I do this:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar = foo orElse { case x => x.toString }
it does not compile: 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[?,?]
But this works fine:
val x: Seq[String] = List(1,2,3).collect { case x => x.toString }
The question is what is the difference? The type of the argument is the same in both cases: PartialFunction[Int, String]
. The value passed in is literally identical. Why one does one case work, but not the other?
回答1:
You need to specify the type for bar
because the compiler is unable to infer it. This compiles:
val foo = PartialFunction[Int, String] { case 1 => "foo" }
val bar : (Int => String) = foo orElse { case x => x.toString }
回答2:
In the case of List(1,2,3).collect{case x => x.toString}
the compiler is able to infer the input type of the partial function based off of how theList
was typed.
final override def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[List[A], B, That])
Based on the type parameters the compiler can infer that you are passing a correctly typed partial function. That's why List(1,2,3).collect{case x:String => x.toString}
does not compile nor does List(1,2,3).collect{case x:Int => x.toString; case x: String => x.toString}
.
Since List
is covariant the compiler is able to infer that the partial function {case x => x.toString}
is a partial function on Int
. You'll notice that List(1,2,3).collect{case x => x.length}
does not compile because the compiler is inferring that you're operating on either an Int
or a subclass of Int
.
Also keep in mind that the {case x => x.toString}
is just syntactic sugar. If we do something like the below then your example works as expected
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.
So the only logical answer from my perspective is that the syntactic sugar that is able to generate a PartialFunction
instance for {case x => x.toString}
does not have enough information at compile time to be able to adequately type it as a PartialFunction[Int, String]
in your orElse
case.
回答3:
You can use the library 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")
}
来源:https://stackoverflow.com/questions/38359522/anonymous-partialfunction-syntax