Dinamikus mixin Scala - ez lehetséges?

szavazat
39

Azt szeretném elérni, amelynek megfelelő végrehajtás

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

Lehet tudni, hogy mit B, de nem tudom, hogy mi van egy (de ha B egy önálló típust, majd én is hozzá némi korlátok A). A Scala fordító elégedett a fenti aláírás, de még nem tudtam rájönni, hogyan végrehajtása nézne - ha ez egyáltalán lehetséges.

Néhány lehetőség jutott eszembe:

  • Használata reflexiós / dinamikus proxy.
    • Legegyszerűbb esetben: A jelentése felület Java szint + I példányosíthatnánk B és nincs önálló típus. Azt hiszem, nem lenne túl nehéz (kivéve, ha befut egy csúnya, váratlan problémák):
      hozzon létre egy új B (b), valamint egy proxy végrehajtási mind A, mind a B és a fohász felvezető átadják a 'vagy' b.
    • Ha B nem lehet példányai még tudtam létrehozni egy alosztálya, és csinálni, mint azt fentebb leírtuk. Ha ez is egy önálló típus én valószínűleg szükség van néhány delegáció itt-ott, de ez még mindig működik.
    • De mi van, ha egy olyan konkrét típus, és nem tudom megtalálni a megfelelő felület ez?
    • Vajon befut több problémát (pl valami kapcsolódó linearizálást, vagy speciális konstrukciókat segítő Java átjárhatóság)?
  • Használata egyfajta csomagolópapír helyett mixin és visszatér a B [A], egy megközelíthető b.
    Sajnos ebben az esetben a hívó azt kell tudni, hogy a fészkelő történik, ami elég kényelmetlen, ha a keverés / csomagolás történik több alkalommal (D [C [B [A]]]), mint kellene találni a megfelelő szintű fészkelő eléréséhez szükséges funkciókat, ezért én nem tartom a megoldást.
  • Megvalósítása fordító plugin. Van nulla tapasztalata vele, de én érzésem az, hogy nem lenne triviális. Szerintem Kevin Wright AutoProxy plugin egy kicsit hasonlít cél, de ez nem lenne elég bajom (még?).

Van más ötlet, hogy működhet? Merre kíván ajánlani? Milyen „kihívások” számíthat?
Vagy azt felejtsd el, mert nem lehet a jelenlegi Scala megszorítások?

Szándék, az én problémám: Mondjuk van egy üzleti munkafolyamat, de ez nem túl szigorú. Néhány lépést rögzített sorrendben, de mások nem, de a végén mindet el kell végezni (vagy egy részük szükséges további feldolgozás).
Egy kicsit több konkrét példát: Van egy A, tudok hozzá a B és C hozzá. Nem érdekel, ami történik, az első, de a végén már szüksége lesz egy A és B C.

Megjegyzés: Nem tudom, hogy túl sokat Groovy de SO bukkant fel ezt a kérdést , és azt hiszem, ez többé-kevésbé ugyanaz, mint amit én szeretnék, legalábbis koncepcionális.

A kérdést 15/07/2010 10:29
a forrás felhasználó
Más nyelveken...                            


2 válasz

szavazat
25

Úgy vélem, ez lehetetlen csinálni szigorúan a futás, mert vonások keverednek fordításkor új Java osztályokat. Ha keverjük össze egy tulajdonság egy meglévő osztályú névtelenül láthatjuk, nézi a classfiles és használata javap, hogy egy névtelen, név-megcsonkított osztály által létrehozott 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 visszatér

Foo.class Main$.class Spam$class.class Main$$anon$1.class Main.class Spam.class

Míg javap Main\$\$anon\$1visszatér

Compiled from "mixin.scala"

public final class Main$$anon$1 extends Foo implements Spam{
    public int eggs();
    public Main$$anon$1();
}

Mint látható, scalac létrehoz egy új, névtelen osztályt, amely betöltött; feltehetően az eljárás eggsezen névtelen osztály létrehoz egy példányt Spam$class, és kéri eggsrajta, de nem vagyok teljesen biztos.

Azonban , tehetünk egy szép hacky trükk itt:

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

Mivel szükség van , hogy használja a Scala fordító, AFAIK, ez valószínűleg közel van a legtisztább megoldás, amit tehetünk, hogy ezt. Elég lassú, de memoization valószínűleg sokat segít.

Ez a megközelítés elég nevetséges, hacky, és ellenkezik a gabonát a nyelvet. Gondolom mindenféle furcsaság hibákat is bebújik; emberek, akik használják a Java hosszabb, mint én figyelmezteti a téboly, hogy jön a szórakozni classloaders.

Válaszolt 17/07/2010 00:15
a forrás felhasználó

szavazat
3

Azt akartam, hogy képes kialakítani Scala bab az én tavaszi alkalmazás keretében, de azt is akartam, hogy képes meghatározni a mixinek felveendő az épített bab:

<?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>

A nehézség az, hogy Class.forName funkció nem engedi meg, hogy adja meg a mixinek. A végén, azt kiterjesztette a fenti hacky megoldást Scala 2.9.1. Tehát itt van a teljes véres; beleértve bit tavasz.

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)
  }

}

A kód még nem foglalkozik a konstruktőrök paraméterekkel és nem másolja a kommentárokat a szülő osztály konstruktora (amennyiben azt csinálni?). Azonban ez ad nekünk egy jó kiindulási pont, hogy használható-e az scala tavaszi névtérben. Természetesen nem csak hogy szavamnak, ellenőrzésképpen egy Specs2 leírás:

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
  }

}
Válaszolt 23/01/2012 14:14
a forrás felhasználó

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more