Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala中的动态混合-可能吗?_Scala_Mixins - Fatal编程技术网

Scala中的动态混合-可能吗?

Scala中的动态混合-可能吗?,scala,mixins,Scala,Mixins,我想要实现的是为 def dynamix[A, B](a: A): A with B 我可能知道B是什么,但不知道A是什么(但如果B有一个自类型,那么我可以在A上添加一些约束)。 scala编译器对上面的签名很满意,但我还不能弄清楚实现是什么样子的——如果可能的话 我想到了一些选择: 使用反射/动态代理。 最简单的情况是:A是Java级别的接口+I可以实例化B,并且它没有自类型。我想这不会太难(除非我遇到一些讨厌的、意想不到的问题): 创建一个新的B(B),以及一个同时实现a和B并使用调用

我想要实现的是为

def dynamix[A, B](a: A): A with B
我可能知道B是什么,但不知道A是什么(但如果B有一个自类型,那么我可以在A上添加一些约束)。 scala编译器对上面的签名很满意,但我还不能弄清楚实现是什么样子的——如果可能的话

我想到了一些选择:

  • 使用反射/动态代理。
    • 最简单的情况是:A是Java级别的接口+I可以实例化B,并且它没有自类型。我想这不会太难(除非我遇到一些讨厌的、意想不到的问题):
      创建一个新的B(B),以及一个同时实现a和B并使用调用处理程序委托给a或B的代理
    • 如果B不能被实例化,我仍然可以创建它的一个子类,并按照上面描述的那样做。如果它也有一个自我类型,我可能需要一些授权在这里和那里,但它可能仍然有效
    • 但是如果A是一个具体的类型,而我找不到合适的接口怎么办
    • 我会遇到更多的问题吗(例如,与线性化有关的问题,或者有助于Java互操作性的特殊构造)
  • 使用一种包装而不是混合并返回B[a],a可从B访问。
    不幸的是,在这种情况下,调用方将需要知道嵌套是如何完成的,如果混合/封装进行了几次(D[C[B[A] ] ],这将是相当不方便的,因为它需要找到正确的嵌套级别来访问所需的功能,所以我不认为它是解决方案。
  • 实现一个编译器插件。我对此没有任何经验,但我的直觉是,这不会是微不足道的。我认为Kevin Wright的插件有一点类似的目标,但它还不足以解决我的问题
你还有其他可行的想法吗?你推荐哪条路?期待什么样的“挑战”?
或者我应该忘记它,因为在当前的Scala约束下这是不可能的

我的问题背后的意图: 假设我有一个业务工作流程,但它不太严格。一些步骤有固定的顺序,但其他步骤没有,但最后必须完成所有步骤(或部分步骤需要进一步处理)。
一个更具体的例子:我有一个A,我可以加上B和C。我不在乎先做哪一个,但最后我需要A加B加C


注释:我对Groovy了解不多,但我觉得它和我想要的差不多,至少在概念上是一样的。

我认为这在运行时是不可能严格做到的,因为特性在编译时被混合到新的Java类中。如果匿名地将一个trait与现有类混合,您可以看到,通过查看类文件并使用javap,scalac创建了一个匿名、名称混乱的类:

class Foo {
  def bar = 5
}

trait Spam {
  def eggs = 10
}

object Main {
  def main(args: Array[String]) = {
    println((new Foo with Spam).eggs)
  }
}
scalac Mixin.scala;ls*.class
返回

Compiled from "mixin.scala"

public final class Main$$anon$1 extends Foo implements Spam{
    public int eggs();
    public Main$$anon$1();
}
Foo.class Main$.class垃圾邮件$class.class
Main$$anon$1.class Main.class Spam.class

javap Main\$\$anon\$1
返回

Compiled from "mixin.scala"

public final class Main$$anon$1 extends Foo implements Spam{
    public int eggs();
    public Main$$anon$1();
}
如您所见,scalac创建了一个新的匿名类,该类在运行时加载;大概这个匿名类中的
eggs
方法创建了
Spam$class
的一个实例,并在其上调用
eggs
,但我不能完全确定

然而,我们可以在这里做一个非常简单的技巧:

import scala.tools.nsc._;
import scala.reflect.Manifest

object DynamicClassLoader {
  private var id = 0
  def uniqueId = synchronized {  id += 1; "Klass" + id.toString }
}

class DynamicClassLoader extends 
    java.lang.ClassLoader(getClass.getClassLoader) {
  def buildClass[T, V](implicit t: Manifest[T], v: Manifest[V]) = {

    // Create a unique ID
    val id = DynamicClassLoader.uniqueId

    // what's the Scala code we need to generate this class?
    val classDef = "class %s extends %s with %s".
      format(id, t.toString, v.toString)

    println(classDef)

    // fire up a new Scala interpreter/compiler
    val settings = new Settings(null)
    val interpreter = new Interpreter(settings)

    // define this class
    interpreter.compileAndSaveRun("<anon>", classDef)

    // get the bytecode for this new class
    val bytes = interpreter.classLoader.getBytesForClass(id)

    // define the bytecode using this classloader; cast it to what we expect
    defineClass(id, bytes, 0, bytes.length).asInstanceOf[Class[T with V]]
  }

}


val loader = new DynamicClassLoader

val instance = loader.buildClass[Foo, Spam].newInstance
instance.bar
// Int = 5
instance.eggs
// Int = 10
导入scala.tools.nsc.\;
导入scala.reflect.Manifest
对象动态类加载器{
私有变量id=0
def uniqueId=synchronized{id+=1;“Klass”+id.toString}
}
类DynamicClassLoader扩展
java.lang.ClassLoader(getClass.getClassLoader){
def buildClass[T,V](隐式T:Manifest[T],V:Manifest[V])={
//创建一个唯一的ID
val id=DynamicClassLoader.uniqueId
//生成这个类需要什么Scala代码?
val classDef=“类%s使用%s扩展了%s”。
格式(id、t.toString、v.toString)
println(classDef)
//启动新的Scala解释器/编译器
val设置=新设置(空)
val解释器=新解释器(设置)
//定义这个类
解释器.compileAndSaveRun(“,classDef)
//获取这个新类的字节码
val bytes=解释器.classLoader.getBytesForClass(id)
//使用这个类加载器定义字节码;将其转换为我们所期望的
defineClass(id,bytes,0,bytes.length).asInstanceOf[Class[T with V]]
}
}
val loader=新的DynamicClassLoader
val instance=loader.buildClass[Foo,Spam].newInstance
instance.bar
//Int=5
例如:鸡蛋
//Int=10
因为您需要使用Scala编译器AFAIK,所以这可能是实现这一点所能做的最干净的解决方案。这相当慢,但记忆可能会有很大帮助


这种方法非常荒谬、老套,而且与语言的本质背道而驰。我想象各种奇怪的虫子会爬进来;使用Java的时间比我长的人警告说,在类加载器上乱搞会带来疯狂。

我希望能够在Spring应用程序上下文中构造Scala bean,但我也希望能够指定要包含在构造的bean中的mixin:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:scala="http://www.springframework.org/schema/scala"
  xsi:schemaLocation=...>

  <scala:bean class="org.cakesolutions.scala.services.UserService" >
    <scala:with trait="org.cakesolutions.scala.services.Mixin1" />
    <scala:with trait="org.cakesolutions.scala.services.Mixin2" />

    <scala:property name="dependency" value="Injected" />
  <scala:bean>
</beans>

另一种方法(没有充分考虑到是否可行):使用
Dynamix[a,B]
(或嵌套的
Dynamix[a,Dynamix[B,C]]
)包装,并通过隐式展开以认知地卸载客户机代码。必须定义每个嵌套深度(无任意深度)。缺点:不会强制执行自类型约束或允许通过自类型进行类型间的交互。+1是一个很好的技巧,但我觉得我至少会遇到一个问题:如何指定在最后我想要一个A和B以及C或Dynamix[A,Dynamix[B,C]],但我不关心混合顺序?我也会很高兴使用Dynamix[A,Dynamix[C,B]],我认为要想出这样一个方法并不太容易
class ScalaBeanFactorySpec extends Specification {

  "getTypedObject mixes-in the specified traits" in {
    val f1 = new ScalaBeanFactory(classOf[Cat],
                                  Seq(classOf[Speaking], classOf[Eating]))

    val c1 = f1.getTypedObject[Cat with Eating with Speaking]

    c1.isInstanceOf[Cat with Eating with Speaking] must_==(true)

    c1.speak    // in trait Speaking
    c1.eat      // in trait Eating
    c1.meow     // in class Cat
  }

}