Taig
Taig

Reputation: 7288

How to pattern-match a generic type argument?

I'm currently unable to wrap my head around Scala's TypeTag reflection API. There are very little information to find on the web and all trial and error attempts are leading nowhere.

def doStuff[T: TypeTag]( param: String ): SomeStuff[E] =
{
    val t = typeOf[T]

    if( <t extends one specific Trait from my application and is an object> )
    {
        <retrieve that companion object and return one of its values>
    }
    else
    {
        t match
        {
             case x if x =:= typeOf[String] => doOtherStuff[String]( param )
             case x if x =:= typeOf[Int] => doOtherStuff[Int]( param )
             ...
        }
    }
}

The pattern matching with Scala's predefined Types is working. However I didn't manage to check if the supplied generic argument inherits my specific Trait and later on retrieve the companion object of the actual class behind T. Trying with a straight forward typeOf[MyTrait[_, T]] is being rejected by the compiler telling me that no TypeTag is available for MyTag. How do I create it?

In addition the Trait's nasty generic signature MyTrait[M <: MyTrait[M, E], E <: Entity[M, E]] is exacerbating the whole thing.

Besides some helpful ideas to solve this problem I highly appreciate any further reading links (I've read all on SO though).

Upvotes: 2

Views: 1934

Answers (1)

gzm0
gzm0

Reputation: 14842

Use the following to test if it is an object (from this question):

typeOf[Test] <:< typeOf[Singleton]

You can use existential types to get a TypeTag of your trait:

typeOf[MyTrait[M, T] forSome { type M <: MyTrait[M, T] }]

This works if you have a TypeTag[T] in scope and is bound as following:

T <: Entity[_,T]

Minimal example:

trait Entity[M, E]
trait MyTrait[M <: MyTrait[M, E], E <: Entity[M, E]]

class MyEnt extends Entity[Test, MyEnt]
class Test extends MyTrait[Test, MyEnt]

def getType[T <: Entity[_, T] : TypeTag] =
  typeOf[MyTrait[M,T] forSome { type M <: MyTrait[M,T] }]

typeOf[Test] <:< getType[MyEnt]
//| res0: Boolean = true

However, this will not work in your case, since T is not properly bounded. Hence you'll have to test against this (with some help from here):

val mts = typeOf[MyTrait[_,_]].typeSymbol

typeOf[Test].baseType(mts) match {
  case TypeRef(_, _, List(_, t)) if t <:< typeOf[MyEnt] => true
  case _ => false
}

Upvotes: 2

Related Questions