2
votes

Imagine a class A that is abstract and has a set of case classes. We don't know how many set classes are or even the name those case classes have.

abstract class A(field: String)
//Example of a case class that extends A
case class B(f: String) extends A(f)

Now I have this:

a match {
    case B(f) => println("f")
}

And I want to pass the case class type by an argument into a method. The reason I want this is because I will configure a set of rules in a file. And I want to load those rules and use pattern matching with some info those rules provide. I want to do something like this:

def printer (a: A, B: A) = {
   a match{
       case B(f) => println("f")
   }
}

Is this possible?

If it isn't that easy can I use an abstract class in pattern matching? It would be perfect if I could simply use the abstract class since it has the main structure of all case classes.

EDIT:

Forgot to mention that case classes can have different arguments so it would be good to use something based on class A (since I can do my pattern matching with the field only)

3
I agree with you, but If I do that this will become a kind of a huge post. This way I only focus on the necessary.Tiago Almeida

3 Answers

5
votes

Not like you tried it. But if you use a Manifest as context bound, you can make it work:

scala> trait Foo
defined trait Foo

scala> case class Bar(baz: String) extends Foo
defined class Bar

scala> def boo[A <: Foo : Manifest](foo: Foo) =
     |   if (foo.getClass isAssignableFrom manifest[A].erasure) "foo" else "boo"
boo: [A <: Foo](foo: Foo)(implicit evidence$1: Manifest[A])java.lang.String

scala> boo[Foo](Bar(""))
res0: java.lang.String = boo

scala> boo[Bar](Bar(""))
res1: java.lang.String = foo
1
votes

You can extract unapply method from the companion object of your case class and use it:

scala> :paste
// Entering paste mode (ctrl-D to finish)

abstract class A(field: String)
case class B(f: String) extends A(f)
case class C(f: String) extends A(f)
case class E(f: String, f1: Int) extends A(f)
case class F(f: String, f1: Int) extends A(f)

class Unapplyer[T: Manifest, R](f: T => Option[R]) {
  def unapply(a: Any): Option[R] = {
    if (manifest[T].erasure.isInstance(a)) f(a.asInstanceOf[T]) else None
  }
}

def printer[T: Manifest, R](a: A, b: T => Option[R]) {
  val P = new Unapplyer(b)
  a match {
    case P((f, f1)) => println(f + " - " + f1)
    case P(f) => println(f)
    case _ => println("oops")
  }
}

// Exiting paste mode, now interpreting.

defined class A
defined class B
defined class C
defined class E
defined class F
defined class Unapplyer
printer: [T, R](a: A, b: (T) => Option[R])(implicit evidence$2: Manifest[T])Unit

scala> printer(B("test"), B.unapply _ )
test

scala> printer(B("test"), C.unapply _ )
oops

scala> printer(E("test", 1), E.unapply _ )
test - 1

scala> printer(E("test", 1), F.unapply _ )
oops

scala> printer(E("test", 1), B.unapply _ )
oops

UPD: Added usage with a variable number and type of arguments.

0
votes

I don't get why you want to pass the type into the method. When you use pattern matching you check the type anyway. So why pass it in? You can just write

abstract class A(field: String)
case class B(f: String) extends A(f)
case class C(i: Int) extends A(i.toString)

def printer (a: A) = {
   a match{
       case B(f) => println(f)
       case C(i) => println(i)
   }
}