I was going through some exercises and noticed the following behavior for matching of tuple2. Is there a particular reason for this?
def test(x: Any): Unit= x match{
case i: Int => println("int")
case b: Boolean => println("bool")
case ti: (_, Int) => println("tuple2 with int")
case tb: (_, Boolean)=> println("tuple2 with boolean")
case _ => println("other")
}
test(false) //prints bool
test(3) ///prints int
test((1,3)) //prints tuple with int
test((1,true)) //prints tuple with int
If i exchange the ti and tb cases, then the (1,3) prints tuple2 with boolean. I assume there is some type casting going on here, but I'm unclear why.
Can someone give me a quick explanation.
Thanks
Type erasure. It can't tell what the types are inside the Tuple at runtime. It will compile fine, but it should emit a warning. This is what happens when I do it in :paste mode in the REPL:
scala> :paste
// Entering paste mode (ctrl-D to finish)
def test(x: Any): Unit= x match{
case i: Int => println("int")
case b: Boolean => println("bool")
case ti: (_, Int) => println("tuple2 with int")
case tb: (_, Boolean)=> println("tuple2 with boolean")
case _ => println("other")
}
// Exiting paste mode, now interpreting.
<console>:10: warning: non-variable type argument Int in type pattern (_, Int) is unchecked since it is eliminated by erasure
case ti: (_, Int) => println("tuple2 with int")
^
<console>:11: warning: non-variable type argument Boolean in type pattern (_, Boolean) is unchecked since it is eliminated by erasure
case tb: (_, Boolean)=> println("tuple2 with boolean")
^
<console>:11: warning: unreachable code
case tb: (_, Boolean)=> println("tuple2 with boolean")
^
test: (x: Any)Unit
Notice the last warning, it says the (_, Boolean) is unreachable because the (_, Int) will match on every Tuple2, courtesy of type erasure.
def test(x: Any) {
x match {
case xi: Int => println("int [" + xi + "]")
case yb: Boolean => println("boolean [" + yb + "]")
case (x, y # (y1: Int)) => println("[" + x + ", int(" + y + ")]")
case (x, y # (y1: Boolean)) => println("[" + x + ", boolean(" + y + ")]")
case _ => println("anything else")
}
}
test(1);
test(true);
test("hello", 1);
test("hello", false);
it seems to work this way.
However, just case (x, y # Int) doesn't work, even thought it complies.
You can try this, it works fine with minimal changes to your code. By unpacking and typing, the function works fine.
def test: Any => Unit = _ match{
case i: Int => println("int")
case b: Boolean => println("bool")
case (x:Any, y: Boolean)=> println("tuple2 with boolean")
case (x:Any, y: Int) => println("tuple2 with int")
case _ => println("other")
}
Related
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"
See (more details, more details, more details):
scala> val v = Some(9).map { case lst: List[_] => lst; case i: Int => List() }
<console>:7: error: scrutinee is incompatible with pattern type;
found : List[_]
required: Int
val v = Some(9).map { case lst: List[_] => lst; case i: Int => List() }
Since you are using Some(9), the compiler knows that the element being mapped is an Int. A List[_] can never be an Int, so the compiler is telling you that you are doing something that doesn't make sense.
If you want the compiler to treat it as a Some[Any], you'll have to be explicit about the type:
val v = Some(9: Any).map { case lst: List[_] => lst; case i: Int => List() }
// v: Option[List[Any]] = Some(List())
or, more likely:
val x: Option[Any] = Some(9)
val v = x.map { case lst: List[_] => lst; case i: Int => List() }
But, for the record, you are probably doing something you shouldn't be and you should rethink your code.
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]
Suppose I have
def foo(x: Any) = x match {
case s: String => println(0)
case i: Int => println(1)
case l: Long => println(2)
//...
}
Is there any way to make something like the following?
def foo(x: Any) = x match {
case s: String => println(0)
case i: Numeric => println("Numeric")
}
You could match against the Number interface:
def foo(x: Any) = x match {
case s: String => println(0)
case i: java.lang.Number => println("Numeric")
}
You could try this:
def foo[A](x: A)(implicit num: Numeric[A] = null) = Option(num) match {
case Some(num) => println("Numeric: " + x.getClass.getName)
case None => println(0)
}
Then this
foo(1)
foo(2.0)
foo(BigDecimal(3))
foo('c')
foo("no")
will print
Numeric: java.lang.Integer
Numeric: java.lang.Double
Numeric: scala.math.BigDecimal
Numeric: java.lang.Character
0
Note that obtaining a null implicit parameter would not mean that no such implicit exist, but just that none was found at compile time in the search scope for implicits.
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.