Anonymous PartialFunction syntax

妖精的绣舞 提交于 2019-12-12 11:47:03

问题


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

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