How to explain these pattern matching examples?

84 Views Asked by At

I wrote some events in FSM, and discovered something I can not explain when pattern matching. I thought the following was completely legal, that is that I can send this actor either a message which is a vector[A] or vector[B].

when(State) {
    case Event(content: Vector[A], _) => {
      println("matched A")
      stay
   }
   case Event(content: Vector[B], _) => {
     println("matched B")
     stay
  }  
}

However, if I send the actor a vector[B] message it leads to

java.lang.ClassCastException: B cannot be cast to A

So basically it tries to match the first event eventhough the next would match.

I tried to make an even simpler pattern match example;

object Pattern extends App {
    val n = Vector(1,2,3)
    val s = Vector("S", "S", "S")
    n match{
       case e:Vector[String] => println("matched string")
       case v:Vector[Int] => println("matched int")
   }

}

This is actually not legal;

Error:(8, 12) pattern type is incompatible with expected type;
found   : Vector[String]
required: scala.collection.immutable.Vector[Int]
case e:Vector[String] => println("matched string")

However, I am allowed to run my code if I do the following cast;

object Pattern extends App {
  val n = Vector(1,2,3).asInstanceOf[Vector[Any]]
  val s = Vector("S", "S", "S")
  n match{
    case e:Vector[String] => println(n(0).getClass)
    case v:Vector[Int] => println("matched int")
  }
}

The thing I find strange then is that I apparently say that Any could match a String, but the print is java.lang.Integer. So should I think of it as I have an vector[Int] that I say is a Vector[Any], since Vector[Any] could be a Vector[String] it matches that pattern, and again since it really is a vector[Int] I mask as Vector[Any] the print is fine too.

Could someone explain these pattern matching observations?

and how should I set up the messages so my state can handle both messages of Vector[A] and Vector[B]?

1

There are 1 best solutions below

0
On BEST ANSWER

Due to type erasure of jvm type information is lost at runtime this kind of pattern matching (pattern matching with higher kinded types) is not supported directly.

Here are the ways to get around this problem

Instead I recommend you to wrap the vector in another container.

sealed trait Vectors

case class VectorString(vs: Vector[String]) extends Vectors

case class VectorInt(vi: Vector[Int]) extends Vectors

def doStuff(v: Vectors) = v match {
 case VectorString(vs) => //be sure that vs is Vector[String]
 case VectorInt(vi) => 
}

Ways to pattern match generic types in Scala

Using TypeTag

import scala.reflect.runtime.universe._

def handle[A: TypeTag](a: A): Unit =
  typeOf[A] match {
    case t if t =:= typeOf[List[String]] =>
      // list is a string list
      val r = a.asInstanceOf[List[String]].map(_.length).sum
      println("strings: " + r)

    case t if t =:= typeOf[List[Int]] =>
      // list is an int list
      val r = a.asInstanceOf[List[Int]].sum
      println("ints: " + r)

    case _ => // ignore rest
  }

val ints: List[Int] = Nil

handle(List("hello", "world")) // output: "strings: 10"
handle(List(1, 2, 3))          // output: "ints: 6"
handle(ints)                   // output: "ints: 0" it works!