2016-08-05 4 views
3

У меня есть две черты, и их объекты экземпляра, определенная следующим образом:Scala отражения разрешить общие черты от имени во время выполнения

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 
} 

Я хочу, чтобы получить моноидные экземпляры во время выполнения от имен. Forexample, если я говорю "min", я хочу MinMonoid объект, "avg" должен дать AvgMonoid объект и т.д. Так что я есть следующие настройки:

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[T](adtName)} 
    instance <- Option { 
     mirror 
     .reflectModule(mirror.staticModule(monoidName)) 
     .instance 
     .asInstanceOf[Monoid[T] with ADTHelper[T]] 
    } 
    } yield instance 
} 

Теперь проблема: я могу сделать:

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

но я не могу сделать:

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

, так как тип monoid позже есть Monoid[Nothing] with ADTHelper[Nothing]. Я не могу разрешить базовый тип T из trait Monoid[T] и trait ADTHelper[T] с использованием метода resolve, с которым я столкнулся. Как я могу изменить функцию resolve так, чтобы она разрешала черты с базовым типом T ???

Я знаю, если я позвоню с помощью resolve[Double](...), он будет работать, но я хочу, чтобы это разрешалось во время выполнения из AGGREGATORS_ADT_DICT.

ответ

2

Разрешение T во время выполнения невозможно, потому что там is no T, чтобы решить тогда! Когда вы пишете resolve("min"), компилятор имеет, чтобы определить аргумент своего типа во время компиляции. И поскольку аргументы ничего не говорят о T, и контекст не дает ожидаемого типа (например, val monoid: Monoid[Double] = resolve("min"), он будет выводить Nothing. И обратите внимание, что код resolve здесь не имеет значения, только его тип подписи.

вы могли бы сделать resolve макрос, определяя, таким образом T на время выполнения макроса, то есть во время компиляции программы с помощью этого макроса. Но это будет работать только в случае, вы знаете имя в компиляции (например, это буквальная строка).

+0

Спасибо за комментарий. Так что это из-за стирания в JVM, верно? Любой альтернативный подход, который вы можете предложить для этого? – bistaumanga

+0

На самом деле, я ant, чтобы получить эти Карты из файла конфигурации. Я не уверен, что использование макросов будет работать, потому что я хочу, чтобы эти черты были расширены позже (за пределами этой кодовой базы) и заставляют эту работу просто предоставлять Карты в файле конфигурации и предоставлять объекты, которые расширяют эти черты. – bistaumanga

+1

Даже если JVM не стирает типы, аргументы типа все равно должны быть определены во время компиляции для проверки типов. –

Смежные вопросы