lundi 19 septembre 2016

What is the proper way to wrap isInstanceOf[] calls?

I'd like to build a wrapper for isInstanceOf[T] and asInstanceOf[T] pair that would output Option[T] with convenient map and getOrElse methods.

So I give a try, but the result has disappointed me.

import scala.reflect.runtime.universe.{TypeTag, typeOf}

class Base()
class Deep() extends Base
class Deeper() extends Deep()

final case class WrapSimple[T](source : T) {
  def cast[U] : Option[U] =
    if (source.isInstanceOf[U]) Some(source.asInstanceOf[U]) else None
}

final case class WrapFullTagged[T: TypeTag](source : T) {
  def cast[U : TypeTag] : Option[U] =
    if (typeOf[T] <:< typeOf[U]) Some(source.asInstanceOf[U]) else None
}

final case class WrapHalfTagged[T](source : T) {
  val stpe = {
    val clazz = source.getClass
    val mirror = scala.reflect.runtime.universe.runtimeMirror(clazz.getClassLoader)
    mirror.classSymbol(clazz).toType
  }
  def cast[U : TypeTag] : Option[U] =
    if (stpe <:< typeOf[U]) Some(source.asInstanceOf[U]) else None
}

object Test {
  val base = new Base
  val deep = new Deep
  val deeper = new Deeper
  val wile : Deep = new Deeper

  def testSimple() : Unit = {
    println(WrapSimple(deep).cast[Base].isDefined) // should be true
    println(WrapSimple(deep).cast[Deeper].isDefined) // should be false
    println(WrapSimple(wile).cast[Deeper].isDefined) // should be true
  }

  def testFullTagged() : Unit = {
    println(WrapFullTagged(deep).cast[Base].isDefined) // should be true
    println(WrapFullTagged(deep).cast[Deeper].isDefined) // should be false
    println(WrapFullTagged(wile).cast[Deeper].isDefined) // should be true
  }

  def testHalfTagged() : Unit = {
    println(WrapHalfTagged(deep).cast[Base].isDefined) // should be true
    println(WrapHalfTagged(deep).cast[Deeper].isDefined) // should be false
    println(WrapHalfTagged(wile).cast[Deeper].isDefined) // should be true
  }

  def testAll() : Unit = {
    testSimple()
    testFullTagged()
    testHalfTagged()
  }
}

WrapSimple looks good, but just does not work, it erases the U type in the isInstanceOf[U] method application, so it is always responds with true. The funny thing is that asInstanceOf[U] keeps the U type normally, so it just produces runtime exceptions.

The second approach I had tried is WrapFullTagged that employs type tags. It seems clear enough, but again plainly breaks the contract. It could only check static types at compile time and has zero insight about actual types in runtime.

So, I breed both approaches and gave birth to the third, that at least produces correct output. But it looks awful and invokes power of reflection that comes with a great cost.

Is it possible to solve the issue with greater elegance?





Aucun commentaire:

Enregistrer un commentaire