How to get around this scala compiler erasure warning? - scala

I am trying to compile this scala code and getting the following compiler warnings.
scala> val props: Map[String, _] =
| x match {
| case t: Tuple2[String, _] => {
| val prop =
| t._2 match {
| case f: Function[_, _] => "hello"
| case s:Some[Function[_, _]] => "world"
| case _ => t._2
| }
| Map(t._1 -> prop)
| }
| case _ => null
| }
<console>:10: warning: non-variable type argument String in type pattern (String, _) is unchecked since it is eliminated by erasure
case t: Tuple2[String, _] => {
^
<console>:14: warning: non-variable type argument _ => _ in type pattern Some[_ => _] is unchecked since it is eliminated by erasure
case s:Some[Function[_, _]] => "world"
^
The answers given on How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections? seems to point to this same issue. But I couldn't infer a solution in this particular context.

Use case t # (_: String, _) instead of case t: Tuple2[String, _] and case s # Some(_: Function[_, _]) instead of case s:Some[Function[_, _]].
Scala cannot match on type parameters.

I would rewrite it like this:
x match {
case (name, method) => {
val prop =
method match {
case f: Function[_, _] => "hello"
case Some(f: Function[_, _]) => "world"
case other => other
}
Map(name -> prop)
}
case _ => null
}

Related

Pattern matching an object (Any) to a Scala Map

I am using a Java library which returns an object of type Object. Now I want to pattern match and get the appropriate type. I expect it to be a Java Map. So, I tried using this:
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.collection.JavaConverters._
val any: Any = new java.util.HashMap[Object, Object]
Option(any).flatMap {
case x: java.util.Map[_, _] => Some(x.asScala.toMap)
case x: Map[_, _] => Some(x)
case _ => None
}
// Exiting paste mode, now interpreting.
<console>:17: error: no type parameters for method flatMap: (f: Any => Option[B])Option[B] exist so that it can be applied to arguments (Any => Option[scala.collection.immutable.Map[_,Any]] forSome { type _ })
--- because ---
argument expression's type is not compatible with formal parameter type;
found : Any => Option[scala.collection.immutable.Map[_,Any]] forSome { type _ }
required: Any => Option[?B]
Option(any).flatMap {
^
<console>:17: error: type mismatch;
found : Any => Option[scala.collection.immutable.Map[_,Any]] forSome { type _ }
required: Any => Option[B]
Option(any).flatMap {
^
Not sure what I am doing wrong here.
The below works. The compiler doesn't have the enough information to derive the type here since we are using existential types for our Maps in the pattern matching. This is because unlike Java Map is not a type in scala but Map[T,U] is
Option(any).flatMap[Any]({
case x: java.util.Map[_, _] => Some(x.asScala.toMap)
case x: Map[_, _] => Some(x)
case _ => None
})
If we dont use existential type as shown below, we would be able to use the flatMap without the explicit type parameter specified
scala> Option(any).flatMap({
| case x: java.util.Map[Int, Int] #unchecked => Some(x.asScala.toMap) // using Int as example to create a fully qualified type of Map
| case x: Map[Int, Int] #unchecked => Some(x) // using Int as example to create a fully qualified type of Map
| case _ => None
| })
res5: Option[scala.collection.immutable.Map[Int,Int]] = Some(Map())

Pattern Matching/Checking Arity of Function1

Given the following method:
scala> def f: List[Any] => Any = xs => 1234 // this output does not matter
f: List[Any] => Any
Is it possible to pattern match on List[Any] => Any? I don't see an unapply method on Function1, so I believe the answer is no.
Here's what I'm trying to do:
Example:
def foo(x: Any) = x match {
case ... // to handle the case of List[Any] => Any]?
case ...
}
Perhaps I can figure out the arity of x: Any to differentiate between List[Any] => Any versus everything else (_)?
EDIT:
I hope that I do not have to rely on f.toString == <function1>.
No, you can't match exactly on List[Any] => Any due to type erasure, but you can match on Function1 itself:
def foo(x: Any): String = x match {
case _: Function1[_, _] => "some function1"
case _ => "other"
}
Any other matching, like case _: (List[Any] => Any) => "function from list to any" will act the same as case _: Function1[_, _] => "some function":
scala> def foo(x: Any): String = x match {
| case _: (List[Any] => Any) => "function from list to any"
| case _ => "other"
| }
<console>:8: warning: non-variable type argument List[Any] in type pattern List[Any] => Any is unchecked since it is eliminated by erasure
case _: (List[Any] => Any) => "function from list to any"
^
foo: (x: Any)String
scala> def aaa(l: Any): Any = null //it's `Any => Any` - not `List[Any] => Any`!!
aaa: (l: Any)Any
scala> foo(aaa _)
res11: String = function from list to any
scala> foo(1)
res12: String = other
You can match purely on type, even without an unapply method:
def foo(x: Any): String = x match {
case _: (Any => Any) => "some function1"
case _ => "other"
}
And then you can check:
foo(f) //"some function1"
foo(1) //"other"

Understanding Erasure with Generic Case Class

For the following case class:
scala> case class Foo[T](name: String) {}
defined class Foo
scala> val foo = Foo[Int]("foo")
foo: Foo[Int] = Foo(foo)
Why will Scala let me, as I think it's doing, match on Foo[Int]? Isn't the Int erased?
scala> foo match {
| case _: Foo[Int] => "foo"
| case _ => "bar"
| }
res2: String = foo
But it shows a compile-time error when including another pattern match case?
scala> foo match {
| case _: Foo[String] => "string"
| case _: Foo[Int] => "int"
| case _ => "other"
| }
<console>:12: warning: non-variable type argument String in type pattern Foo[String] is unchecked since it is eliminated by erasure
case _: Foo[String] => "string"
^
<console>:12: error: pattern type is incompatible with expected type;
found : Foo[String]
required: Foo[Int]
case _: Foo[String] => "string"
^
class SuperFoo;
case class Foo[T](name: String) extends SuperFoo {}
val foo: SuperFoo = Foo[Int]("foo")
foo match {
case _: Foo[String] => "foo"
case _ => "bar"
} //> res0: String = foo + warning
In your case compiler knows exact type of foo.
It is erased. In your case compiler can statically check that foo is Foo[Int] and match expression here has sense only with Foo[Int], Foo[_] or Any (AnyRef, scala.Product, scala.Serializable).
But if you hide foo real class using, for example, base class Any:
val foo: Any = Foo[Int]("foo")
val res = foo match {
case _: Foo[String] => "string"
case _ => "other"
}
println(res) // string
You will get warning:
non-variable type argument String in type pattern Foo[String] is
unchecked since it is eliminated by erasure
and program will print "string".

Scala: Using a TypeTag to match on a Some's type

See the following code:
def createOption[T: TypeTag](referentialData: Any) : Option[T] = {
Option(referentialData) match {
case Some(camelMessage: CamelMessage) => {
Option(camelMessage.body) match {
case Some(option: T) => Some(option)
case _ => None
}
}
case _ => None
}
}
Basically I am looking to return an Option[T] if camelMessage.body is non-null and of type T.
The uses of Option(referentialData) is effectively referentialData != null
Likewise for Option(camelMessage.body)
How do I use the TypeTag to determine if camelMessage.body is of type T.
(I know this can be re-written to not use TypeTags and Options but I want to learn how to use TypeTags so please no suggestions to re-write, thanks!)
Edit
I tried a new approach as could not find a solution for the above, but could not get this one to work either:
def createOption[T](referentialData: Any) : Option[T] = {
Option(referentialData) match {
case Some(option) => Try(option.asInstanceOf[T]).toOption
case _ => None
}
}
When I invoke this using createOption[Long]("test") I was presuming to get a None back, but instead I got a Some(String)
Where am I going wrong here?
This is a duplicate of this one.
But you want to try it with ClassTag to show the limitation:
scala> def f[A: ClassTag](x: Any): Option[A] = x match {
| case y: A => println("OK"); Some(y) ; case _ => println("Nope"); None }
f: [A](x: Any)(implicit evidence$1: scala.reflect.ClassTag[A])Option[A]
scala> f[String]("foo")
OK
res0: Option[String] = Some(foo)
scala> f[Long](2L)
Nope
res1: Option[Long] = None
scala> f[java.lang.Long](new java.lang.Long(2L))
OK
res2: Option[Long] = Some(2)
scala> def f[A: TypeTag](x: Any): Option[A] = Option(x) match {
| case Some(y: A) => println("OK"); Some(y) ; case _ => println("Nope"); None }
<console>:51: warning: abstract type pattern A is unchecked since it is eliminated by erasure
case Some(y: A) => println("OK"); Some(y) ; case _ => println("Nope"); None }
^
f: [A](x: Any)(implicit evidence$1: reflect.runtime.universe.TypeTag[A])Option[A]

How to test a value on being AnyVal?

Tried this:
scala> 2.isInstanceOf[AnyVal]
<console>:8: error: type AnyVal cannot be used in a type pattern or isInstanceOf test
2.isInstanceOf[AnyVal]
^
and this:
scala> 12312 match {
| case _: AnyVal => true
| case _ => false
| }
<console>:9: error: type AnyVal cannot be used in a type pattern or isInstanceOf test
case _: AnyVal => true
^
The message is very informative. I get that I can't use it, but what should I do?
I assume you want to test if something is a primitive value:
def testAnyVal[T](x: T)(implicit evidence: T <:< AnyVal = null) = evidence != null
println(testAnyVal(1)) // true
println(testAnyVal("Hallo")) // false
println(testAnyVal(true)) // true
println(testAnyVal(Boolean.box(true))) // false
I assume that your type is actually Any or you'd already know whether it was AnyVal or not. Unfortunately, when your type is Any, you have to test all the primitive types separately (I have chosen the variable names here to match the internal JVM designations for the primitive types):
(2: Any) match {
case u: Unit => println("Unit")
case z: Boolean => println("Z")
case b: Byte => println("B")
case c: Char => println("C")
case s: Short => println("S")
case i: Int => println("I")
case j: Long => println("J")
case f: Float => println("F")
case d: Double => println("D")
case l: AnyRef => println("L")
}
This works, prints I, and does not give an incomplete match error.