2010-04-09 9 views
6

Ich möchte Objektinstanzen als Module/functors verwenden, mehr oder weniger, wie unten dargestellt:Wie benutzt man Objekte als Module/Funktoren in Scala?

abstract class Lattice[E] extends Set[E] { 
    val minimum: E 
    val maximum: E 
    def meet(x: E, y: E): E 
    def join(x: E, y: E): E 
    def neg(x: E): E 
} 

class Calculus[E](val lat: Lattice[E]) { 
    abstract class Expr 
    case class Var(name: String) extends Expr {...} 
    case class Val(value: E) extends Expr {...} 
    case class Neg(e1: Expr) extends Expr {...} 
    case class Cnj(e1: Expr, e2: Expr) extends Expr {...} 
    case class Dsj(e1: Expr, e2: Expr) extends Expr {...} 
} 

, dass ich für jedes Gitter eine andere Kalkül Instanz erstellen können also (die Operationen Ich brauche die Informationen, von denen durchführen wird sind die maximalen und minimalen Werte des Gitters). Ich möchte in der Lage sein, Ausdrücke desselben Kalküls zu mischen, aber Ausdrücke verschiedener Ausdrücke nicht mischen zu dürfen. So weit, ist es gut. Ich kann meine Kalkülinstanzen erstellen, aber das Problem ist, dass ich keine Funktionen in anderen Klassen schreiben kann, die sie manipulieren.

Zum Beispiel versuche ich einen Parser zu erstellen, um Ausdrücke aus einer Datei zu lesen und sie zurückzugeben; Ich habe auch versucht, einen Zufallsgenerator zu schreiben, um ihn in meinen Tests mit ScalaCheck zu verwenden. Es stellt sich heraus, dass jedes Mal, wenn eine Funktion ein Expr-Objekt generiert, ich es nicht außerhalb der Funktion verwenden kann. Auch wenn ich die Calculus-Instanz erzeuge und sie als Argument an die Funktion übergebe, die ihrerseits die Expr-Objekte generiert, wird die Rückgabe der Funktion nicht als vom selben Typ der außerhalb der Funktion erzeugten Objekte erkannt.

Vielleicht ist mein Englisch nicht klar genug, lassen Sie mich ein Spielzeugbeispiel von dem versuchen, was ich tun möchte (nicht der echte ScalaCheck Generator, aber nahe genug).

def genRndExpr[E](c: Calculus[E], level: Int): Calculus[E]#Expr = { 
    if (level > MAX_LEVEL) { 
    val select = util.Random.nextInt(2) 
    select match { 
     case 0 => genRndVar(c) 
     case 1 => genRndVal(c) 
    } 
    } 
    else { 
    val select = util.Random.nextInt(3) 
    select match { 
     case 0 => new c.Neg(genRndExpr(c, level+1)) 
     case 1 => new c.Dsj(genRndExpr(c, level+1), genRndExpr(c, level+1)) 
     case 2 => new c.Cnj(genRndExpr(c, level+1), genRndExpr(c, level+1)) 
    } 
    } 
} 

Nun, wenn ich versuche, den obigen Code ich viele

 
error: type mismatch; 
found : plg.mvfml.Calculus[E]#Expr 
required: c.Expr 
     case 0 => new c.Neg(genRndExpr(c, level+1)) 

zu kompilieren Und das gleiche passiert, wenn ich versuche, wie etwas zu tun:

val boolCalc = new Calculus(Bool) 
val e1: boolCalc.Expr = genRndExpr(boolCalc) 

Bitte beachten Sie, dass Der Generator selbst ist nicht von Belang, aber ich muss viel Ähnliches tun (dh Kalkulationsexemplarausdrücke erstellen und bearbeiten), was den Rest des Systems betrifft.

Mache ich etwas falsch? Ist es möglich zu tun, was ich tun möchte?

Hilfe zu diesem Thema wird dringend benötigt und geschätzt. Vielen Dank im Voraus.


Nach Erhalt einer Antwort von Apocalisp und versuchen es.

Vielen Dank für die Antwort, aber es gibt immer noch einige Probleme. Die vorgeschlagene Lösung war die Signatur der Funktion zu ändern:

def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr 

ich die Unterschrift für alle Funktionen geändert beteiligt: ​​getRndExpr, getRndVal und getRndVar.Und ich habe die gleiche Fehlermeldung überall, wo ich diese Funktionen aufrufen und bekam die folgende Fehlermeldung:

 
error: inferred type arguments [Nothing,C] do not conform to method genRndVar's 
type parameter bounds [E,C <: plg.mvfml.Calculus[E]] 
     case 0 => genRndVar(c) 

Da der Compiler schien nicht in der Lage zu sein, die richtigen Typen, um herauszufinden, ich alle Funktionsaufruf geändert wie unten sein:

case 0 => new c.Neg(genRndExpr[E,C](c, level+1)) 

Danach wird auf den ersten 2 Funktionsaufrufe (genRndVal und genRndVar) gab es keine Kompilierung Fehler, sondern auf den folgenden 3 Anrufe (rekursive Aufrufe zu genRndExpr), wobei die Rückkehr der Funktion verwendet wird, ein bauen neues Expr-Objekt Ich habe folgende Fehlermeldung erhalten:

 
error: type mismatch; 
found : C#Expr 
required: c.Expr 
     case 0 => new c.Neg(genRndExpr[E,C](c, level+1)) 

Also, wieder, ich stecke fest. Jede Hilfe wird geschätzt.

+0

Ihr Fragetitel ist ein bisschen irreführend. Betrachten Sie etwas wie "Wie bezieht man innere Klassen von expernal relativ zum Klassenraum?" – Alexey

Antwort

3

Das Problem ist, dass Scala die beiden Typen Calculus[E]#Expr und Calculus[E]#Expr nicht vereinheitlichen kann.

Die sehen zwar gleich aus, oder? Nun, bedenken Sie, dass Sie zwei verschiedene Kalküle über einen Typ E haben könnten, jede mit ihrem eigenen Expr Typ. Und Sie würden Ausdrücke der zwei nicht mischen wollen.

Sie müssen die Typen so einschränken, dass der Rückgabetyp Expr denselben Typ hat wie der Expr Innentyp Ihres Calculus Arguments. Was Sie tun müssen, ist dies:

def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr 
+0

Hallo! Vielen Dank für die Antwort, aber es gibt immer noch einige Probleme. Ich habe deine Lösung versucht. Ich habe die Signatur für alle beteiligten Funktionen geändert: getRndExpr, getRndVal und getRndVar. Und ich bekomme die gleiche Fehlermeldung überall dort, wo ich diese Funktionen anrufe. Ich kann das Problem hier aus Platzmangel nicht vollständig beschreiben, also werde ich den Körper der Frage bearbeiten, um richtig antworten zu können. – Jeff

1

Wenn Sie nicht möchten, dass ein bestimmtes Kalkül von Calculus abzuleiten dann nur Expr zu globalem Bereich verschieben oder es durch globale Reichweite verweisen:

class Calculus[E] { 
    abstract class Expression 
    final type Expr = Calculus[E]#Expression 

    ... the rest like in your code 
} 

this question bezieht sich genau auf das gleiche Problem.

Wenn Sie einen Subtyp von Calculus machen wollen und neu definieren Expr dort (was unwahrscheinlich ist), müssen Sie:

getRndExpr in die Calculus Klasse setzen oder setzen getRndExpr in einer abgeleiteten Eigenschaft:

trait CalculusExtensions[E] extends Calculus[E] { 
    def getRndExpr(level: Int) = ... 
    ... 
} 

beziehen Sie sich auf this Thread für den Grund, warum so.