복붙노트

[SCALA] 스칼라의 동적 믹스 인 - 그것이 가능할까요?

SCALA

스칼라의 동적 믹스 인 - 그것이 가능할까요?

내가 달성하고자하는 대한 적절한 구현을 가지고있다

def dynamix[A, B](a: A): A with B

나는 B가 무엇인지 알 수 있지만, A는 모르는 (그러나 B가 자기를 입력 한 다음 경우 나는에 대한 몇 가지 제약 조건을 추가 할 수 있습니다). 스칼라 컴파일러는 위의 서명에 만족,하지만 난 아직 구현의 모습 방법을 알아낼 수 없습니다 - 그것은 모든 가능한 경우.

내 마음에 와서 몇 가지 옵션 :

당신이 일을 할 수있는 다른 아이디어가 있습니까? 당신은 어느 쪽을 추천 하시겠습니까? "도전"의 어떤 종류를 기대 하는가? 는 현재 스칼라 제약 할 수 없기 때문에 아니면 내가, 그것을 잊지해야합니까?

내 문제 뒤에 의도 : 나는 비즈니스 워크 플로우를 말해봐,하지만 너무 엄격한 아니다. 일부 단계는 순서가 고정되어 있지만, 다른 사람은하지 않습니다 만, 마지막에 그들 모두 수행해야합니다 (또는 그들 중 일부는 추가 처리를 위해 필요합니다). 좀 더 구체적인 예 : 내가하는 A를 가지고, 나는 그것을 B와 C를 추가 할 수 있습니다. 내가 먼저 수행되는 상관 없어,하지만 결국에 나는 C.와 B로는 A가 필요합니다

코멘트 : 나는 그루비에 대해 너무 많이 알고 있지만, 그래서이 질문을 팝업 나는 적어도 개념적으로는, 내가 원하는 것과 더 많거나 적은 같은 추측하지 않습니다.

해결법

  1. ==============================

    1.나는 특성이 새로운 자바 클래스로 컴파일시에 혼합되어 있기 때문에이 런타임에 엄격하게 수행하는 것은 불가능하다 생각합니다. 기존 클래스와 특성을 혼합하는 경우 익명으로 당신은 익명의 이름 망가 클래스가 scalac에 의해 생성되는의 클래스 파일에서 찾고은 javap를 사용하여 볼 수 있습니다 :

    나는 특성이 새로운 자바 클래스로 컴파일시에 혼합되어 있기 때문에이 런타임에 엄격하게 수행하는 것은 불가능하다 생각합니다. 기존 클래스와 특성을 혼합하는 경우 익명으로 당신은 익명의 이름 망가 클래스가 scalac에 의해 생성되는의 클래스 파일에서 찾고은 javap를 사용하여 볼 수 있습니다 :

    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 반환

    foo.class 위 홈페이지 $는 스팸 $의 class.class을을 .class 홈페이지 $$ 곧 $ 1.class Main.class가 Spam.class

    은 javap 홈페이지 \ $ \ $ 곧 \ $ 1 명을 반환하는 동안

    Compiled from "mixin.scala"
    
    public final class Main$$anon$1 extends Foo implements Spam{
        public int eggs();
        public Main$$anon$1();
    }
    

    당신이 볼 수 있듯이, scalac는 런타임에로드 된 새로운 익명 클래스를 만듭니다; 아마도이 익명 클래스의 메소드 계란 스팸 $ 클래스의 인스턴스를 생성하고 그 위에 계란을 호출,하지만 난 완전히 확실하지 않다.

    그러나, 우리는 여기에 꽤 해키 트릭을 할 수 있습니다 :

    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
    

    당신은 스칼라 컴파일러를 사용할 필요가 있기 때문에, AFAIK, 이것은 당신이 얻을 할 수있는 깨끗한 솔루션 아마 가깝습니다. 그것은 아주 천천히,하지만 메모이 제이션은 아마 크게 도움이 될 것이다.

    이 방법은 해키, 꽤 말도, 그리고 언어의 성미에 간다. 나는 크리프 수있는 이상한 버그의 모든 종류의 상상; 나보다 더 자바를 사용하는 사람들은 클래스 로더와 장난와 함께 제공되는 광기의 경고한다.

  2. ==============================

    2.내 Spring 애플리케이션 컨텍스트에서 스칼라 콩을 구성 할 수 있기를 원하지만, 나는 또한 건설 콩에 포함 할 유지 mixin을 지정할 수 있기를 원 :

    내 Spring 애플리케이션 컨텍스트에서 스칼라 콩을 구성 할 수 있기를 원하지만, 나는 또한 건설 콩에 포함 할 유지 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>
    

    어려움은 Class.forName을 기능이 나를 유지 mixin을 지정하는 것을 허용하지 않는다는 것입니다. 결국, 나는 스칼라 2.9.1 위의 해키 솔루션을 확장했다. 그래서, 여기에 그것의 전체 피투성이에; 봄의 비트를 포함.

    class ScalaBeanFactory(private val beanType: Class[_ <: AnyRef],
                           private val mixinTypes: Seq[Class[_ <: AnyRef]]) {
      val loader = new DynamicClassLoader
      val clazz = loader.buildClass(beanType, mixinTypes)
    
       def getTypedObject[T] = getObject.asInstanceOf[T]
    
       def getObject = {
         clazz.newInstance()
       }
    
       def getObjectType = null
       def isSingleton = true
    
    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: Class[_ <: AnyRef], vs: Seq[Class[_ <: AnyRef]]) = {
        val id = DynamicClassLoader.uniqueId
    
        val classDef = new StringBuilder
    
        classDef.append("class ").append(id)
        classDef.append(" extends ").append(t.getCanonicalName)
        vs.foreach(c => classDef.append(" with %s".format(c.getCanonicalName)))
    
        val settings = new Settings(null)
        settings.usejavacp.value = true
        val interpreter = new IMain(settings)
    
    
        interpreter.compileString(classDef.toString())
    
    
        val r = interpreter.classLoader.getResourceAsStream(id)
        val o = new ByteArrayOutputStream
        val b = new Array[Byte](16384)
        Stream.continually(r.read(b)).takeWhile(_ > 0).foreach(o.write(b, 0, _))
        val bytes = o.toByteArray
    
        defineClass(id, bytes, 0, bytes.length)
      }
    
    }
    

    코드는 아직 매개 변수를 생성자를 처리 할 수없는 부모 클래스의 생성자에서 주석을 복사하지 않습니다 (그것은 그렇게해야합니까?). 그러나, 그것은 우리에게 스칼라 봄 네임 스페이스에서 사용할 수있는 좋은 출발점을 제공합니다. 물론, 그냥 내 말을 걸릴 Specs2 사양에 그것을 확인하지 :

    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
      }
    
    }
    
  3. from https://stackoverflow.com/questions/3254232/dynamic-mixin-in-scala-is-it-possible by cc-by-sa and MIT license