2017-02-07 1 views
1

Ich habe den folgenden Code;Force-Typ-Parameter zu einem Merkmal

object main 
{ 
    def main(args: Array[String]): Unit = 
    { 
     trait E 
     { 
      def test(): Unit = println("test :)") 
     } 

     class B[T](val x: Int) 
     { 
      def inc(): B[T] with T = new B[T](x + 1) with T 
     } 

     class A[T](f : B[T] with T => Unit) 
     { 
      def apply(b: B[T] with T) = f(b) 
     } 

     val b = new B[E](0) with E 
     val a = new A[E](b => b.test())(b) 
    } 
} 

jedoch die Linie def inc(): B[T] with T = new B[T](x + 1) with T nicht kompiliert, um den Fehler zu geben, dass „Klassentyp erforderlich, aber T gefunden“ und „T muss ein Merkmal sein, in mischen“. Ich verstehe, warum dies der Fall ist, aber ich kann keinen Weg finden, um es zu umgehen! Ich habe keinen Weg zu Constraint T gefunden, um eine Eigenschaft zu sein, die mich Angst macht, dass dieser Ansatz nicht funktioniert ...

Um mehr Hintergrund zu geben, warum ich versuche, dies zu erreichen Jeder kann eine bessere Lösung anbieten) Ich habe eine Parsec[S, U, E, A] Klasse, die aus Funktionen besteht, die ein State[S, U, E] with E Objekt akzeptieren. Die Idee ist, dass U ist ein Benutzer gegeben Zustand, A ist das Ergebnis des Parsers, S ein Strom von Token und E ist eine Erweiterung des Staates (zum Beispiel könnte man eine Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]] usw. usw. erstellen möchten. Die U = Int wäre etwas, das der Benutzer zum Beispiel zählen wollte, und das sollte nicht in den Zustand eingreifen müssen, der für die Indentationsempfindlichkeit erforderlich ist (was zwei Ints ist), die durch Mischen des Merkmals IndentationSensitive bereitgestellt würde, wenn der Benutzer eine andere Funktionalität wünschte sie können weiter mischen in mehr Eigenschaften für die Parser.

Also, gibt es sowieso ich kann den Typ Parameter T in den Code, so dass ich kann es in eine B mischen, oder wenn nicht, gibt es einen besseren Weg zu erreichen, was ich brauche?

Wenn es wirklich nicht klar, was ich versuche dann this Frage auf Codereview zu erreichen stellt die Situation (in einem viel mehr Details). Aber Parsec[S <: Stream[_, _], U, A] wird durch Parsec[S <: Stream[_, _], U, E, A] und das gleiche für State und alle anderen Teile ersetzt.

+0

Ich weiß es nicht sicher, aber ich erwarte, dass dies unmöglich ist. Mixins werden zur Kompilierzeit behandelt, aber dies würde eine dynamische Mischung erfordern. – puhlen

+0

aber sicherlich könnte der Typ zur Kompilierzeit bekannt sein? Irgendwann wirst du einen konkreten Typ erreichen, wenn du die Typen genug auswählst? –

+0

Dies ist nicht möglich. –

Antwort

0

Um etwas mehr Hintergrund zu geben, warum ich versuche, diese (nur für den Fall jemanden eine bessere Lösung anbieten) zu erreichen, ich habe eine Parsec[S, U, E, A] Klasse, die aus Funktionen eingebaut, die ein State[S, U, E] with E Objekt übernehmen. Die Idee ist, dass U ein Benutzer gegebenen Zustand ist, A das Ergebnis des Parsers ist, S ein Strom von Tokens und E einen gewissen Erweiterung des Zustands (zum Beispiel könnte man wünschen, ein Parsec[Stream[String, Char], Int, IndentationSensitive, List[Expr]]

In erstellen In diesem Fall würde ich einfach ein val extension: E Feld zu State hinzufügen und ändern Sie die Funktionen zu akzeptieren State[S, U, E].Wenn Sie wirklich wollen, können Sie eine implizite Konvertierung von State[S, U, E] zu E hinzufügen, so dass die Funktionen direkt auf E Mitglieder zugreifen können, aber wahrscheinlich würde es nicht selbst tun.

0

Ich habe es geschafft, eine Lösung für das Problem zu finden, es ist nicht ideal, aber es geht um ein paar andere Probleme mit dem System. Nämlich, wenn wir eine new State[S, U, E](input, pos, state) with E erstellen, was mit den Variablen geschehen soll, die mit E hinzugefügt wurden. Sie gehen verloren und das ist ein Mörder.

Definieren wir einen neuen Typ type StateBuilder[S <: Stream[_, _], U, E] = (Option[State[S, U, E] with E], S, SourcePos, U) => State[S, U, E] with E. Dies ist eine Funktion, die einen neuen Zustand des gewünschten Typs unter Berücksichtigung eines möglichen vorherigen Zustandes und einiger neuer Werte für die "normalen" Parameter des Zustands aufbauen kann.

Jetzt können wir den Staat als neu definieren;

case class State[S <: Stream[_, _], U, E](stateInput: S, statePos: SourcePos, stateUser: U, build: StateBuilder[S, U, E]) 

Und jetzt brauchen wir nur einige dieser StateBuilder[S, U, E], die zwischen den Staaten weitergegeben werden, aber wir müssen es in zuzuführen, wenn wir den Ausgangszustand erstellen. Das ist in Ordnung, aber es bedeutet, dass der Benutzer verstehen muss, was sie sind (was ein wenig nachteilig ist). Ein Beispielbuilder für keine Erweiterungen;

trait Default 
object Default 
{ 
    def build[S <: Stream[_, _], U](s: Option[State[S, U, Default] with Default], ts: S, pos: SourcePos, u: U): State[S, U, Default] with Default = 
    { 
     new State[S, U, Default](ts, pos, u, build) with Default 
    } 
} 

und ein komplizierteres könnte sein;

trait IndentationSensitive 
{ 
    var stateLevel: Int = 0 
    var stateRequiredIndent: Int = 0 
} 
object IndentationSensitive 
{  
    def build[S <: Stream[_, _], U](s: Option[State[S, U, IndentationSensitive] with IndentationSensitive], ts: S, pos: SourcePos, u: U): State[S, U, IndentationSensitive] with IndentationSensitive = 
    { 
     val s_ = new State[S, U, IndentationSensitive](ts, pos, u, build) with IndentationSensitive 
     s match 
     { 
      case Some(s) => 
       s_.stateLevel = s.stateLevel 
       s_.stateRequiredIndent = s.stateRequiredIndent 
      case None => 
       s_.stateLevel = 0 
       s_.stateRequiredIndent = 0 
     } 
     s_ 
    } 
} 

Um Erweiterungen zu schreiben, würde der Benutzer die Builder-Funktion zur Hand benötigen konstruieren, aber das ist nicht unvernünftig für jemand in der Lage zu sein, herauszufinden, wie zu tun ist. Es wäre schön, diese für jeden Typ automatisch erstellen zu können, aber das ist eine andere Frage.

Verwandte Themen