混合的特质动态混合的特质动态(Mixing in a trait dynamically)

2019-05-13 17:27发布

有一个特质

trait Persisted {
  def id: Long
}

我怎么落实接受任何情况的类的实例,并与混合特质返回其复制的方法?

该方法的签名如下所示:

def toPersisted[T](instance: T, id: Long): T with Persisted

Answer 1:

这可以用宏(即是正式因为2.10.0-M3斯卡拉的一部分)来完成。 下面是你在找什么要旨的例子 。

1)我的宏生成,从所提供的案例类并坚持继承,就像一个本地类new T with Persisted会做。 然后它会缓存它的参数(防止多种评估),并创建了创建类的实例。

2)我怎么知道要生成的树? 我有一个简单的应用程序,parse.exe,打印,从解析输入的代码产生的AST。 所以,我只是援引parse class Person$Persisted1(first: String, last: String) extends Person(first, last) with Persisted ,指出输出和我的宏复制它。 parse.exe是一个包装scalac -Xprint:parser -Yshow-trees -Ystop-after:parser 。 有不同的方式去探索的AST,更多的“斯卡拉2.10元编程” 。

3)宏扩展可理智核对,如果你提供-Ymacro-debug-lite作为参数scalac。 在这种情况下,所有的扩张将被打印出来,你就可以检测代码生成错误更快。

编辑。 更新的例子为2.10.0-M7



Answer 2:

这是不可能达到你想要使用香草斯卡拉什么。 问题是,像下面的混入:

scala> class Foo
defined class Foo

scala> trait Bar
defined trait Bar

scala> val fooWithBar = new Foo with Bar
fooWithBar: Foo with Bar = $anon$1@10ef717

创建一个Foo with Bar混合,但它不是在运行时完成。 编译器只是生成一个新的匿名类:

scala> fooWithBar.getClass
res3: java.lang.Class[_ <: Foo] = class $anon$1

见Scala的动态混入-这可能吗? 获取更多信息。



Answer 3:

你所要做的是被称为记录串联,一些Scala的类型系统不支持。 (FWIW,存在类型系统-如此和本 -提供此功能)

我想类型类可能适合你的使用情况,但我不能肯定地告诉因为这个问题不提供你想解决什么问题的足够信息。



Answer 4:

更新

你可以找到一个最新的工作液,它利用斯卡拉2.10.0-RC1的工具箱作为API的一部分SORM项目。


下面的解决方案是基于Scala的2.10.0-M3反射API和Scala解释。 它动态地创建和缓存类从原来的case类与混合特质继承。由于在最大缓存这个解决方案应该动态地创建只有一个类为每个原始案例类和以后重新使用它。

由于新的反射API不算多披露也不是稳定的,有它没有教程但这种解决方案可能涉及一些愚蠢的repitative行动和怪癖。

下面的代码使用Scala 2.10.0-M3进行了测试。

1. Persisted.scala

这种特点在混合。 请注意,我已经改变了它一下,由于我的程序更新

trait Persisted {
  def key: String
}

2. PersistedEnabler.scala

实际工作者对象

import tools.nsc.interpreter.IMain
import tools.nsc._
import reflect.mirror._

object PersistedEnabler {

  def toPersisted[T <: AnyRef](instance: T, key: String)
                              (implicit instanceTag: TypeTag[T]): T with Persisted = {
    val args = {
      val valuesMap = propertyValuesMap(instance)
      key ::
        methodParams(constructors(instanceTag.tpe).head.typeSignature)
          .map(_.name.decoded.trim)
          .map(valuesMap(_))
    }

    persistedClass(instanceTag)
      .getConstructors.head
      .newInstance(args.asInstanceOf[List[Object]]: _*)
      .asInstanceOf[T with Persisted]
  }


  private val persistedClassCache =
    collection.mutable.Map[TypeTag[_], Class[_]]()

  private def persistedClass[T](tag: TypeTag[T]): Class[T with Persisted] = {
    if (persistedClassCache.contains(tag))
      persistedClassCache(tag).asInstanceOf[Class[T with Persisted]]
    else {
      val name = generateName()

      val code = {
        val sourceParams =
          methodParams(constructors(tag.tpe).head.typeSignature)

        val newParamsList = {
          def paramDeclaration(s: Symbol): String =
            s.name.decoded + ": " + s.typeSignature.toString
          "val key: String" :: sourceParams.map(paramDeclaration) mkString ", "
        }
        val sourceParamsList =
          sourceParams.map(_.name.decoded).mkString(", ")

        val copyMethodParamsList =
          sourceParams.map(s => s.name.decoded + ": " + s.typeSignature.toString + " = " + s.name.decoded).mkString(", ")

        val copyInstantiationParamsList =
          "key" :: sourceParams.map(_.name.decoded) mkString ", "

        """
        class """ + name + """(""" + newParamsList + """)
          extends """ + tag.sym.fullName + """(""" + sourceParamsList + """)
          with """ + typeTag[Persisted].sym.fullName + """ {
            override def copy(""" + copyMethodParamsList + """) =
              new """ + name + """(""" + copyInstantiationParamsList + """)
          }
        """
      }

      interpreter.compileString(code)
      val c =
        interpreter.classLoader.findClass(name)
          .asInstanceOf[Class[T with Persisted]]

      interpreter.reset()

      persistedClassCache(tag) = c

      c
    }
  }

  private lazy val interpreter = {
    val settings = new Settings()
    settings.usejavacp.value = true
    new IMain(settings, new NewLinePrintWriter(new ConsoleWriter, true))
  }


  private var generateNameCounter = 0l

  private def generateName() = synchronized {
    generateNameCounter += 1
    "PersistedAnonymous" + generateNameCounter.toString
  }


  // REFLECTION HELPERS

  private def propertyNames(t: Type) =
    t.members.filter(m => !m.isMethod && m.isTerm).map(_.name.decoded.trim)

  private def propertyValuesMap[T <: AnyRef](instance: T) = {
    val t = typeOfInstance(instance)

    propertyNames(t)
      .map(n => n -> invoke(instance, t.member(newTermName(n)))())
      .toMap
  }

  private type MethodType = {def params: List[Symbol]; def resultType: Type}

  private def methodParams(t: Type): List[Symbol] =
    t.asInstanceOf[MethodType].params

  private def methodResultType(t: Type): Type =
    t.asInstanceOf[MethodType].resultType

  private def constructors(t: Type): Iterable[Symbol] =
    t.members.filter(_.kind == "constructor")

  private def fullyQualifiedName(s: Symbol): String = {
    def symbolsTree(s: Symbol): List[Symbol] =
      if (s.enclosingTopLevelClass != s)
        s :: symbolsTree(s.enclosingTopLevelClass)
      else if (s.enclosingPackageClass != s)
        s :: symbolsTree(s.enclosingPackageClass)
      else
        Nil

    symbolsTree(s)
      .reverseMap(_.name.decoded)
      .drop(1)
      .mkString(".")
  }

}

3. Sandbox.scala

测试程序

import PersistedEnabler._

object Sandbox extends App {
  case class Artist(name: String, genres: Set[Genre])
  case class Genre(name: String)

  val artist = Artist("Nirvana", Set(Genre("rock"), Genre("grunge")))

  val persisted = toPersisted(artist, "some-key")

  assert(persisted.isInstanceOf[Persisted])
  assert(persisted.isInstanceOf[Artist])
  assert(persisted.key == "some-key")
  assert(persisted.name == "Nirvana")
  assert(persisted == artist)  //  an interesting and useful effect

  val copy = persisted.copy(name = "Puddle of Mudd")

  assert(copy.isInstanceOf[Persisted])
  assert(copy.isInstanceOf[Artist])
  //  the only problem: compiler thinks that `copy` does not implement `Persisted`, so to access `key` we have to specify it manually:
  assert(copy.asInstanceOf[Artist with Persisted].key == "some-key")
  assert(copy.name == "Puddle of Mudd")
  assert(copy != persisted)

}


Answer 5:

虽然它不可能组成一个对象后,它的创作,你可以有非常广泛的测试,以确定是否使用对象类型别名和定义结构是一个特定组成的:

  type Persisted = { def id: Long }

  class Person {
    def id: Long = 5
    def name = "dude"
  }

  def persist(obj: Persisted) = {
    obj.id
  }

  persist(new Person)

与任何对象def id:Long将可算作持续。

实现什么,我认为你正在试图做的是可能的隐式转换:

  object Persistable {
    type Compatible = { def id: Long }
    implicit def obj2persistable(obj: Compatible) = new Persistable(obj)
  }
  class Persistable(val obj: Persistable.Compatible) {
    def persist() = println("Persisting: " + obj.id)
  }

  import Persistable.obj2persistable
  new Person().persist()


文章来源: Mixing in a trait dynamically