em70
em70

Reputation: 6081

Pattern matching on base class and all derived classes in Scala

I'm trying to achieve something like this:

def a(b: Any) = {
  b match {
     case x: Seq[String] => println("x")
  }
}

// somewhere else

a(List("b"))

As a result I'd love to see "x" being printed, and I don't.

Basically I want to match against a type/trait and cover all of the objects whose types derive from/implement such type/trait, with the trait being Seq and the type parameter being known in advance. Since I'm a scala novice I'm quite stuck, however.

Ideas?

Upvotes: 3

Views: 1723

Answers (2)

Dan Burton
Dan Burton

Reputation: 53665

My first thought is that you're doing it wrong. Instead of pattern matching, what you want here is a type class, or some other mechanism (class methods) that can do type-based dynamic dispatch. Using pattern matching to perform your own type-based dispatch makes for messy code and thwarts type safety.

Here's a quick and common example of a type class in Scala. First, define a trait that the class of types has in common:

trait Ord[T] { def compare (x: T, y: T): Boolean }

In your case, you would want the a method to be the trait's method. Then, for each type that you want to belong to the type class, create an implicit object for it:

implicit object ordInt extends Ord[Int] {
  def compare (x: Int, y: Int) = x <= y
}

Here I've made Int an instance of my Ord type class. Now you can write methods that rely on the interface that your type class provides. The method should accept the specific type class object implicitly, like so:

def sort[T](xs: List[T])(implicit ord: Ord[T]) = {

you can then make use of the special methods on the type class by invoking it on the implicit object.

def sort[T](xs: List[T])(implicit ord: Ord[T]) = {
  def insert(y: T, ys: List[T]): List[T] = ys match {
    case Nil => List(y)
    case z :: zs =>
      if (ord.compare(y,z)) { y::z::zs } // <--- here I use ord.compare
      else { z :: insert(y, zs) }
  }
  xs.foldRight(List[T]())(insert _)
}

And voila! We have type-directed dispatch, without losing type safety. Want to sort a list of Ints? No problem. Trying to sort a list of something that doesn't have an Ord instance? The compiler will stop you from shooting yourself in the foot.

Upvotes: 1

kiritsuku
kiritsuku

Reputation: 53348

You can't check against parameterized types because of type erasure. See this question why there will be a warning: Warning about an unchecked type argument in this Scala pattern match?

Another question and its answers tell you how to get around that: How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?

Nevertheless your code works fine, when you don't check against type parameters:

scala> List("a") match { case _: Seq[_] => 1 case _ => 2 }
res0: Int = 1

Upvotes: 5

Related Questions