0

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]?

stian
  • 1,947
  • 5
  • 25
  • 47
  • 1
    What are `A` and `B`? Can you post a [MCVE]? – Yuval Itzchakov Sep 23 '16 at 09:08
  • A and B are case classes. A complete and verifiable example (in the sense that you could run it, would take alot more "unecessary" code for the Akka FSM. The other code would run (copy and past). – stian Sep 23 '16 at 09:16
  • The question isn't really about FSM, it's about pattern matching and types. One could isolate the problem and create a reproducible easily. – Yuval Itzchakov Sep 23 '16 at 11:51
  • @Yuval. I wrote my akka example, and some pattern matching "artifacts" that I thought where related. Since I was not sure how to perfectly translate my akka-based problems into a more simple pattern matching representation, I wrote my question as is. Luckily for me someone was able to contribute with some insights that were valuable to me. – stian Sep 23 '16 at 16:40
  • That's awesome, really. But more generally, it's easier for people to take a non working, complete example and play around with it to understand the issue at hand. Creating one may be difficult, but definitely broadens the audiences ability to help. – Yuval Itzchakov Sep 23 '16 at 17:44
  • I was luckily able to get help from how I presented the problem. So I am happy, and hopefully the Q&A can be helpful for others as well. – stian Sep 23 '16 at 20:20

1 Answers1

1

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!
Nagarjuna Pamu
  • 14,737
  • 3
  • 22
  • 40