vendredi 5 août 2016

Scala reflection to resolve generic trait from name at runtime

I have two traits and their instance objects defined as following :

package reflection

trait Monoid[T] {
  def id: T
  def op(lhs: T, rhs: T): T
}

trait ADTHelper[T]{
  type V
  def create(value: V): T
  def get(adt: T): Any
}

case class Avg(avg: Double, n: Int)

object AvgMonoid extends Monoid[Avg] with ADTHelper[Avg]{
  override def id: Avg = Avg(0, 0)

  override def op(lhs: Avg, rhs: Avg): Avg =
    Avg( (lhs.avg * lhs.n + rhs.avg * rhs.n)/(lhs.n + rhs.n), lhs.n + rhs.n)

  override type V = Double
  override def create(value: Double): Avg = Avg(value, 1)
  override def get(adt: Avg): Any = adt.avg
}

object MinMonoid extends Monoid[Double] with ADTHelper[Double]{
  override def id: Double = Double.MaxValue

  override def op(lhs: Double, rhs: Double): Double = if(lhs < rhs) lhs else rhs

  override type V = Double

  override def create(value: Double): Double = value

  override def get(adt: Double): Any = adt
}

I want to get monoid instances at runtime from names. Forexample, if i say "min", I want MinMonoid object, "avg" should give AvgMonoid object, etc. So i have following setup:

object Test extends App {

  val AGGREGATORS_NAME_DICT = Map(
    "avg" -> "reflection.AvgMonoid",
    "min" -> "reflection.MinMonoid",
    "max" -> "reflection.MaxMonoid"
  )
  val AGGREGATORS_ADT_DICT = Map(
    "avg" -> "reflection.Avg",
    "min" -> "scala.Double",
    "max" -> "scala.Double"
  )

  val mirror = runtimeMirror(getClass.getClassLoader)

  def stringToTypeTag[A](name: String): TypeTag[A] = {
    val tpe = mirror.staticClass(name).selfType
    TypeTag(mirror, new api.TypeCreator {
      def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) =
        if (m eq mirror) tpe.asInstanceOf[U#Type]
        else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
    })
  }

  def resolve[T](fname: String): Option[Monoid[T] with ADTHelper[T]] = for {
    monoidName <- AGGREGATORS_NAME_DICT.get(fname)
    adtName <- AGGREGATORS_ADT_DICT.get(fname)
    tag <- Option{stringToTypeTag(adtName)}
    instance <- Option {
      mirror
        .reflectModule(mirror.staticModule(monoidName))
        .instance
        .asInstanceOf[Monoid[T] with ADTHelper[T]]
    }
  } yield instance
}

Now the problem is: I can do:

println(resolve("min").get.op(1.0, 2.0))

but I can't do:

val monoid = resolve("min").get
println(monoid.op(1.0, 2.0))

because the type of monoid in later is Monoid[Nothing] with ADTHelper[Nothing]. I am unable to resolve the underlying type T of trait Monoid[T] and trait ADTHelper[T] using the resolve method I have come up with. How can I modify the resolve function so that it resolves the traits with Underlying Type T ???

I know If I call using resolve[Double](...), it will work, But I want this to resolve in runtime from the AGGREGATORS_ADT_DICT .





Aucun commentaire:

Enregistrer un commentaire