Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I perform matching on a type parameter in Scala to see if it implements a trait?

Tags:

I would like to have a method which returns a class of a certain type, but I want the method to behave differently depending on whether or not the class extends a particular trait as follows:


case class ClassA extends TraitA
case class ClassB extends TraitB
case class ClassC extends TraitA
...
def myfunc[T]():T = {
  T match {
    case TraitA => // return new T in a particular way 
    case TraitB => // ditto
  }
}

Is this possible, or am I going about it the wrong way?

Thanks

like image 881
codefly Avatar asked Mar 30 '11 11:03

codefly


People also ask

What are the different ways to implement match expressions in Scala?

Using if expressions in case statements First, another example of how to match ranges of numbers: i match { case a if 0 to 9 contains a => println("0-9 range: " + a) case b if 10 to 19 contains b => println("10-19 range: " + b) case c if 20 to 29 contains c => println("20-29 range: " + c) case _ => println("Hmmm...") }

How does match work in Scala?

“match” is always defined in Scala's root class to make its availability to the all objects. This can contain a sequence of alternatives. Each alternative will start from case keyword. Each case statement includes a pattern and one or more expression which get evaluated if the specified pattern gets matched.

Does Scala have pattern matching?

Notes. Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.

What is case class and pattern matching in Scala?

It is defined in Scala's root class Any and therefore is available for all objects. The match method takes a number of cases as an argument. Each alternative takes a pattern and one or more expressions that will be performed if the pattern matches. A symbol => is used to separate the pattern from the expressions.


1 Answers

You can't compare types directly, because there isn't anything there to compare (at runtime, due to erasure). You could work on a representation of your class:

trait TraitA { }
trait TraitB { }
class ClassA extends TraitA { }
class ClassB extends TraitB { }

def myFunc[T](clazz: Class[T]) = {
  if (classOf[TraitA] isAssignableFrom clazz) println("A")
  else if (classOf[TraitB] isAssignableFrom clazz) println("B")
  else println("?")
}

scala> myFunc(classOf[ClassA])
A

scala> myFunc(classOf[String])
?

or you can pattern match on instances of the class:

def myFunc2[T](t: T) = t match {
  case _: TraitA => println("A")
  case _: TraitB => println("B")
  case _ => println("?")
}

scala> myFunc2(new ClassA)
A

scala> myFunc2(Some(5))
?

You can also use the first approach in a syntactically less obtrusive way via class manifests:

def myFunc3[T](implicit mf: ClassManifest[T]) = {
  val clazz = mf.erasure
  if (classOf[TraitA] isAssignableFrom clazz) println("A")
  else if (classOf[TraitB] isAssignableFrom clazz) println("B")
  else println("?")
}

scala> myFunc3[ClassA]
A

scala> myFunc3[String]
?

and you can choose different sorts of dispatch also if the if/else becomes wieldy:

object MyFunc {
  val dispatch = Map(
    classOf[TraitA] -> (() => println("A")),
    classOf[TraitB] -> (() => println("B"))
  )
  val default = () => println("?")
  def apply[T](implicit mf: ClassManifest[T]) = 
    dispatch.find(_._1 isAssignableFrom mf.erasure).map(_._2).getOrElse(default)()
}

scala> MyFunc[ClassA]
A

scala> MyFunc[String]
?

Note that any generic code that you use this from will need to have a class manifest available (either as an implicit parameter or in shorthand, [T: ClassManifest].

like image 191
Rex Kerr Avatar answered Oct 11 '22 06:10

Rex Kerr