2009-02-26 8 views
10

Ich würde gerne wissen, wie die Mitgliedstypen in Scala arbeiten, und wie ich Typen zuordnen soll.Typ Parameter im Vergleich zu Mitgliedstypen in Scala

Ein Ansatz besteht darin, den zugeordneten Typ zu einem Typparameter zu machen. Der Vorteil dieses Ansatzes ist, dass ich die Varianz des Typs vorschreiben kann, und ich kann sicher sein, dass ein Subtyp den Typ nicht ändert. Die Nachteile sind, dass ich den Typparameter nicht vom Typ in einer Funktion ableiten kann. Der zweite Ansatz besteht darin, den zugeordneten Typ zu einem Mitglied des zweiten Typs zu machen, was das Problem hat, dass ich keine Grenzen für die zugeordneten Typen der Subtypen vorschreiben kann und daher den Typ in den Funktionsparametern nicht verwenden kann (wenn x: x, x # T möglicherweise nicht in jeder Beziehung mit xT)

ein konkretes Beispiel wäre:

ich ein Merkmal für DFAs haben (ohne den Typ-Parameter sein könnte)

trait DFA[S] { /* S is the type of the symbols in the alphabet */ 
    trait State { def next(x : S); } 
    /* final type Sigma = S */ 
} 

und ich möchte eine Funktion zum Ausführen erstellen diese DFA über eine Eingangssequenz, und ich möchte

  • die Funktion etwas <% Seq[alphabet-type-of-the-dfa] als Eingangssequenz Typen festgelegt werden
  • die Funktion Anrufer müssen die Art Parameter nicht angeben, müssen alle
  • gefolgert werden, würde ich wie die Funktion, die mit dem konkreten DFA-Typ aufgerufen werden soll (aber wenn es eine Lösung gibt, bei der die Funktion keinen Typparameter für das DFA haben würde, ist es in Ordnung)
  • Die Alphabettypen müssen unbeschränkt sein (d. muss diese als auch für eine noch unbekannte benutzerdefinierte Klasse)
  • die DFAs mit unterschiedlichen Alphabet-Typen sind nicht Subtypen

Ich hat versucht, ein DFA für Char sein:

def runDFA[S, D <: DFA[S], SQ <% Seq[S]](d : D)(seq : SQ) = .... 

dieser Werke , außer der Typ S wird hier nicht abgeleitet, daher muss ich die gesamte Typenparameterliste auf jeder Aufrufseite schreiben.

def runDFA[D <: DFA[S] forSome { type S }, SQ <% Seq[D#Sigma]](... same as above 

dies nicht funktioniert hat (ungültige zirkuläre Referenz D eingeben ??? (was ist das?))

ich auch den Typ Parameter gelöscht, erstellt eine abstrakte Art Sigma und versuchte, diese Art Bindung in den konkreten Klassen. runDFA würde aussehen wie

def runDFA[D <: DFA, SQ <% Seq[D#Sigma]](... same as above 

aber dies führt unweigerlich zu Problemen wie "type mismatch: expected dfa.Sigma, bekam D#Sigma"

Irgendwelche Ideen? Zeiger?

Edit:

Da die Antworten zeigen, gibt es keine einfache Möglichkeit, dies zu tun, könnte jemand mehr erarbeiten, warum ist es nicht möglich, und was geändert werden müssen, damit es funktioniert?

Die Gründe, warum ich runDFA ro eine freie Funktion (keine Methode) sein möchte, ist, dass ich andere ähnliche Funktionen, wie Automatenminimierung, reguläre Sprachoperationen, NFA-zu-DFA-Konvertierungen, Sprachfaktorisierung etc. und alle haben möchte Dies innerhalb einer Klasse ist gegen fast jedes Prinzip des OO-Designs.

+0

Wow, ich dachte du meintest das: http://www.huygens-fokker.org/scala/ – MusiGenesis

Antwort

3

Zunächst einmal, Sie brauchen nicht auf die Parametrisierung SQ <% Seq [ S]. Schreiben Sie den Methodenparameter als Seq [S]. Wenn SQ <% Seq [S] ist, dann ist jede Instanz davon implizit in Seq [S] umwandelbar (was <% bedeutet), so dass der Compiler, wenn er als Seq [S] übergeben wird, automatisch die Umwandlung einfügt.

Darüber hinaus, was Jorge über Typ-Parameter auf D sagte und es zu einer Methode auf DFA halten. Wegen der Art, wie innere Klassen in Scala funktionieren, würde ich dringend raten runDFA auf DFA setzen. Bis das pfadabhängige Schreibmaterial funktioniert, kann der Umgang mit inneren Klassen einer externen Klasse ein wenig schmerzhaft sein.

So, jetzt haben Sie

trait DFA[S]{ 
    ... 

    def runDFA(seq : Seq[S]) = ... 
} 

Und runDFA ist ganz plötzlich ziemlich einfach für Typ-Parameter abzuleiten: Es spielt keine haben.

3

Scala Typ Rückschluss lässt manchmal viel zu wünschen übrig.

Gibt es einen Grund, warum Sie die Methode nicht in Ihrem DFA-Merkmal haben können?

def run[SQ <% Seq[S]](seq: SQ) 

Wenn Sie die D param später nicht benötigen, können Sie auch Ihre Methode, ohne es versuchen, zu definieren:

def runDFA[S, SQ <% Seq[S]](d: DFA[S])(seq: SQ) = ... 
0

Einige nützliche Informationen darüber, wie sich die beiden unterscheidet:

Von der unförmigen guide:

Ohne Typ Parameter, die Sie nicht abhängige Typen machen können, zum Beispiel

trait Generic[A] { 
    type Repr 
    def to(value: A): Repr 
    def from(value: Repr): A 
} 

import shapeless.Generic 
def getRepr[A](value: A)(implicit gen: Generic[A]) = 
    gen.to(value) 

Hier der zurück Art von to ist abhängig vom Eingabetyp A (da der implizierte Wert von A abhängt):

case class Vec(x: Int, y: Int) 
case class Rect(origin: Vec, size: Vec) 
getRepr(Vec(1, 2)) 
// res1: shapeless.::[Int,shapeless.::[Int,shapeless.HNil]] = 1 :: 2 :: 
    HNil 
getRepr(Rect(Vec(0, 0), Vec(5, 5))) 
// res2: shapeless.::[Vec,shapeless.::[Vec,shapeless.HNil]] = Vec(0,0) 
    :: Vec(5,5) :: HNil 

ohne Typ-Mitglieder wäre dies unmöglich sein:

trait Generic2[A, Repr] 
def getRepr2[A, R](value: A)(implicit generic: Generic2[A, R]): R = 
    ??? 

Wir hätten den gewünschten Wert von Repr zu getRepr als Typ Parameter, effec vely machen getRepr nutzlos zu übergeben. Die Intuitive Wegnahme von diesem ist, dass Typparameter als "Eingänge" nützlich sind und Art Mitglieder als "Ausgänge" nützlich sind.

siehe bitte die formlose guide für Details.