Scala pattern corresponds to a function - how to bypass type erasure

I would like the template to match the function, the problem is type erasure. Please note that in the fragment below, despite the fact that a warning is issued, there is a match and an "incorrect" one.

scala> def f1 = ()=>true f1: () => Boolean scala> val fl = f1 fl: () => Boolean = <function0> scala> scala> fl match { | case fp :Function0[Boolean] => 1 | case _ => 2 | } res8: Int = 1 scala> scala> fl match { | case fp :Function0[String] => 1 | case _ => 2 | } <console>:11: warning: fruitless type test: a value of type () => Boolean cannot also be a () => String (but still might match its erasure) case fp :Function0[String] => 1 ^ res9: Int = 1 scala> 

What I could come up with is the case class, which completes this function. I get a security type, note the error below. But , firstly, it is inelegant and secondly, I donโ€™t understand how the case class can apply types, while the pattern match cannot. The only thing I would have was that the case class is protected by the compiler and that a match is only allowed at runtime

 scala> case class FunctionWrapper(fn: ()=>Boolean) defined class FunctionWrapper scala> val fw = FunctionWrapper(fl) fw: FunctionWrapper = FunctionWrapper(<function0>) scala> def fs = ()=>"whatever" fs: () => String scala> val fws = FunctionWrapper(fs) <console>:10: error: type mismatch; found : () => String required: () => Boolean val fws = FunctionWrapper(fs) ^ scala> fw match { | case FunctionWrapper(f) => f() | case _ => false | } res10: Boolean = true 

To summarize, I would like to know if there is an elegant way for a template to combine a function, and perhaps understand why the above examples acted the way they did

+3
function scala pattern-matching
Aug 27 '15 at 10:19
source share
2 answers

Short answer: you need to cancel erasure in order to implement types with TypeTag .

I do not understand how the case class can apply types, whereas pattern matching cannot.

Because your case class has no type parameters. Only general types are erased, so it is called "partial erasure".

Related question: General unapply method for different types of List . The following code is essentially the same as one of the answers, but using functions instead of lists:

 import scala.reflect.runtime.universe._ def foo[A : TypeTag](a: A): Int = typeOf[A] match { case t if t =:= typeOf[Int => Int] => a.asInstanceOf[Int => Int](0) case t if t =:= typeOf[Boolean => Int] => a.asInstanceOf[Boolean => Int](true) case _ => 3 } foo((i: Int) => i + 1) // res0: Int = 1 foo((b: Boolean) => if (b) 2 else 0) // res1: Int = 2 foo((b: Boolean) => !b) // res2: Int = 3 

I'm not sure if there is a way to write an extractor to make the matching block more enjoyable.

If you need to pass these functions in such a way that it loses information about the static type (dragging them to the Function[_, _] collection, using Akka messages, etc.), you also need to pass the tag:

 import scala.reflect.runtime.universe._ case class Tagged[A](a: A)(implicit val tag: TypeTag[A]) def foo[A, B](tagged: Tagged[A => B]): Int = tagged.tag.tpe match { case t if t =:= typeOf[Int => Int] => tagged.a.asInstanceOf[Int => Int](0) case t if t =:= typeOf[Boolean => Int] => tagged.a.asInstanceOf[Boolean => Int](true) case _ => 3 } foo(Tagged((i: Int) => i + 1)) // res0: Int = 1 foo(Tagged((b: Boolean) => if (b) 2 else 0)) // res1: Int = 2 foo(Tagged((b: Boolean) => !b)) // res2: Int = 3 
+4
Aug 27 '15 at 11:30
source share

The warning here is actually twofold:

1) Firstly, "a value of type () => Boolean also cannot be () => String": indeed, you are matched with () => Boolean and can never be at the same time () => String therefore chance does not make sense, and in an ideal world it should never coincide. However, erasure comes into play, as the second part hints at

2) "(but may still coincide with its erasure)": erasure here means that instances () => Boolean (aka Function0[Boolean] ) and instances () => String (aka Function0[String] ) at runtime are displayed in exactly the same way, so there is no way to distinguish between them, and when you map the template to Function0[String] , in reality the compiler can only say that it is some Function0 , but cannot know whether it is Function0[Boolean] or Function0[String] .

In anticipation of the second part of the warning, it was easy to miss here. If fl were printed Any , the first part of the warning would not apply, and you would receive a more useful message:

 scala> (fl:Any) match { | case fp :Function0[Boolean] => 1 | case _ => 2 | } <console>:11: warning: non-variable type argument Boolean in type pattern () => Boolean is unchecked since it is eliminated by erasure case fp :Function0[Boolean] => 1 

As for the solution, you can do little more than delegate an instance of a function. Fortunately, you do not need to write one specific shell for each possible return type. Scala provides ClassTag and TypeTag to get around erasure, and we can use it by storing it in the shell of a (common) function. However, it will be rather cumbersome to use and erroneous on the insecurity side, since you will have to match the ClassTag / TypeTag stored inside the wrapper and pass (either directly through asInstanceOf or indirectly through the same pattern match) a function with the corresponding function type .

+3
Aug 27 '15 at 10:43 on
source share



All Articles