6 Stimmen

Wie man Objekte als Module/Funktoren in Scala verwenden?

Ich möchte Objektinstanzen als Module/Funktoren verwenden, mehr oder weniger wie unten dargestellt:

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 {...}
}

Damit ich für jedes Gitter eine andere Kalkülinstanz erstellen kann (die Operationen, die ich durchführen werde, benötigen die Information, welches die Höchst- und Mindestwerte des Gitters sind). Ich möchte in der Lage sein, Ausdrücke desselben Kalküls zu mischen, aber nicht Ausdrücke verschiedener Kalküle mischen dürfen. So weit, so gut. Ich kann meine Kalkülinstanzen erstellen, aber das Problem ist, dass ich keine Funktionen in anderen Klassen schreiben kann, die diese manipulieren.

Ich versuche zum Beispiel, einen Parser zu erstellen, der Ausdrücke aus einer Datei liest und sie zurückgibt; ich habe auch versucht, einen Generator für zufällige Ausdrücke zu schreiben, den ich in meinen Tests mit ScalaCheck verwenden kann. Es stellt sich heraus, dass ich jedes Mal, wenn eine Funktion ein Expr-Objekt erzeugt, dieses nicht außerhalb der Funktion verwenden kann. Selbst wenn ich die Calculus-Instanz erstelle und sie als Argument an die Funktion übergebe, die wiederum die Expr-Objekte erzeugt, wird die Rückgabe der Funktion nicht als vom gleichen Typ wie die außerhalb der Funktion erstellten Objekte erkannt.

Vielleicht ist mein Englisch nicht klar genug, lassen Sie mich versuchen, ein Spielzeug-Beispiel von dem, was ich tun möchte (nicht die 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))
    }
  }
}

Wenn ich nun versuche, den obigen Code zu kompilieren, erhalte ich eine Vielzahl von

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

Das Gleiche passiert, wenn ich versuche, etwas zu tun wie:

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

Bitte beachten Sie, dass der Generator selbst nicht von Belang ist, aber ich werde ähnliche Dinge tun müssen (d.h. Calculus-Instanzausdrücke erstellen und manipulieren) eine Menge auf dem Rest des Systems.

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

Hilfe in dieser Angelegenheit wird dringend benötigt und ist willkommen. Vielen Dank im Voraus.


Nachdem ich eine Antwort von Apocalisp erhalten und sie ausprobiert habe.

Vielen Dank für die Antwort, aber es gibt 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 habe die Signatur für alle beteiligten Funktionen geändert: getRndExpr, getRndVal und getRndVar. Und überall, wo ich diese Funktionen aufrufe, erhalte ich die gleiche Fehlermeldung und 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 nicht in der Lage zu sein schien, die richtigen Typen herauszufinden, änderte ich alle Funktionsaufrufe, um wie unten zu sein:

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

Danach gab es bei den ersten beiden Funktionsaufrufen (genRndVal und genRndVar) keine Kompilierungsfehler, aber bei den folgenden drei Aufrufen (rekursive Aufrufe von genRndExpr), bei denen die Rückgabe der Funktion verwendet wird, um ein neues Expr-Objekt zu erstellen, erhielt ich den folgenden Fehler:

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

Ich stecke also wieder einmal fest. Jede Hilfe wird geschätzt werden.

3voto

Apocalisp Punkte 34088

Das Problem ist, dass Scala nicht in der Lage ist, die beiden Typen zu vereinheitlichen Calculus[E]#Expr y Calculus[E]#Expr .

Für dich sehen die aber gleich aus, oder? Nun, bedenken Sie, dass Sie zwei verschiedene Kalkulationen über einen Typ haben könnten E jeder mit seinem eigenen Expr Typ. Und Sie sollten die beiden Ausdrücke nicht vermischen.

Sie müssen die Typen so einschränken, dass der Rückgabetyp derselbe ist Expr Typ als die Expr innere Art Ihrer Calculus Argument. Was Sie tun müssen, ist Folgendes:

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

1voto

Alexey Punkte 8727

Wenn Sie keinen spezifischen Kalkül von Calculus ableiten wollen, verschieben Sie Expr einfach in den globalen Bereich oder verweisen Sie über den globalen Bereich auf ihn:

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

    ... the rest like in your code
}

diese Frage bezieht sich auf genau das gleiche Problem.

Wenn Sie einen Untertyp von Calculus erstellen und Expr dort neu definieren wollen (was unwahrscheinlich ist), müssen Sie das tun:

getRndExpr in die Klasse Calculus einfügen oder getRndExpr in einen abgeleiteten Trait einfügen:

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

siehe ce Thread für den Grund, warum das so ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X