1

I have a match statement like this:

val x = y match {
  case array: Array[Float] => call z
  case array: Array[Double] => call z
  case array: Array[BigDecimal] => call z 
  case array: Array[_] => show error
}

How do I simplify this to use only two case statements, since first three case statements do same thing, instead of four.

Pravin Gadakh
  • 603
  • 1
  • 9
  • 19
  • `case _: T | _: U =>` is the syntax. – som-snytt Oct 30 '15 at 07:13
  • Possible duplicate of [How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?](http://stackoverflow.com/questions/1094173/how-do-i-get-around-type-erasure-on-scala-or-why-cant-i-get-the-type-paramete) – Johny T Koshy Oct 30 '15 at 08:59

5 Answers5

1

Type erasure does not really gives you opportunity to understand how array was typed. What you should do instead is to extract head ( first element) of array and check it's type. For example following code works for me:

  List(1,2,3) match {
    case (a:Int) :: tail => println("yep")
  }
vvg
  • 6,325
  • 19
  • 36
  • 2
    Actually type is not erased for arrays. Java arrays, which is the underlying implementation, keep their types. You can see that scalac gives no warnings on the example pattern matching. – thoredge Oct 30 '15 at 09:34
  • I do not see warning in the case of `List` either `val z = List() match { case x: List[Int] => "println" }` – vvg Oct 30 '15 at 09:50
  • That's probably because List() is a list of Nothing and will happily match anything. Try 'List(0.0) match { case x: List[Int] => "println" }' instead' and see how return the same value even though this is a list of double. – thoredge Oct 30 '15 at 10:20
  • Per my understanding matching happened for `Lists` because of type erasure. but we can not do the same of `Array`. Compiler says types are incompatible. – vvg Oct 30 '15 at 10:42
1

This work, although not very nice:

def x(y: Array[_]) = y match {
  case a if a.isInstanceOf[Array[Double]] || 
            a.isInstanceOf[Array[Float]] || 
            a.isInstanceOf[Array[BigDecimal]] => "call z"
  case _ => "show error"
}

Would have thought that pattern matching with "|" as below would do the trick. However, this gives pattern type is incompatible with expected type on Array[Float] and Array[BigDecimal]. It might be that matching of generic on this single case where it could work has not been given so much attention:

def x(y: Array[_ <: Any]) = y match {
  case a @ (_:Array[Double] | _:Array[Float] | _:Array[BigDecimal]) => "call z"
  case a: Array[_] => "show error"
}
thoredge
  • 12,237
  • 1
  • 40
  • 55
0

May be it helps a bit:

import reflect.runtime.universe._

object Tester {
 def test[T: TypeTag](y: Array[T]) = y match {
   case c: Array[_] if typeOf[T] <:< typeOf[AnyVal] => "hi"
   case c: Array[_] => "oh"
 }
}

scala> Tester.test(Array(1,2,3))
res0: String = hi

scala> Tester.test(Array(1.0,2.0,3.0))
res1: String = hi

scala> Tester.test(Array("a", "b", "c"))
res2: String = oh
codejitsu
  • 3,162
  • 2
  • 24
  • 38
  • I don't tested it with alternative match patterns, but I think it is possible to write something like: "case c: Array[_] if typeOf[T] <:< typeOf[Double] || if typeOf[T] <:< typeOf[Integer]". – codejitsu Oct 30 '15 at 07:34
  • 1
    typeOf[T] <:< typeOf[AnyVal] matches with all of scala.Double, scala.Float, scala.Long, scala.Int, scala.Char, scala.Short, and scala.Byte, scala.Unit and scala.Boolean – Hüseyin Zengin Oct 30 '15 at 07:57
  • @HüseyinZengin yes, and this is the reason I wrote my comment above – codejitsu Oct 30 '15 at 09:36
  • This is only useful if you know the type of the array statically, for the compiler to insert the `TypeTag`. If you just have `c: Array[_]`, it won't work. – Alexey Romanov Oct 30 '15 at 12:57
0

You can obtain the class of array elements as follows (it will be null for non-array types): c.getClass.getComponentType. So you can write:

if (Set(classOf[Float], classOf[Double], classOf[BigDecimal]).contains(c.getClass.getComponentType)) {
  // call z
} else {
  // show error
}

Not particularly Scala'ish, though; I think @thoredge's answer is the best for that.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
0

You could also check whether the Array is empty first and then if not, just pattern match on Array.head...something like:

def x(y: Array[_]) = {
    y.isEmpty match {
      case true => "error"
      case false => y.head match {
         case a:Double | a:BigInt => do whatever
         case _ => "error"  
      }
    }
}
Jonny Coombes
  • 575
  • 2
  • 6