2010-04-09 10 views
6

chcę użyć instancji obiektu jako moduły/funktory, pokazane mniej więcej jak poniżej:Jak używać obiektów jako modułów/funktorów w Scali?

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

Tak, że mogę stworzyć inną instancję nazębnego dla każdego kraty (operacje będę wykonywał potrzebują informacji, które są maksymalną i minimalną wartością sieci). Chcę móc wymieszać wyrażenia z tego samego rachunku, ale nie można mieszać wyrażeń różnych. Jak na razie dobrze. Mogę tworzyć instancje rachunku różniczkowego, ale problem polega na tym, że nie mogę pisać funkcji w innych klasach, które nimi manipulują.

Na przykład próbuję utworzyć analizator składni do odczytywania wyrażeń z pliku i zwracania ich; Próbowałem również napisać generator losowych wyrażeń do wykorzystania w moich testach z ScalaCheck. Okazuje się, że za każdym razem, gdy funkcja generuje obiekt Expr, nie mogę go użyć poza funkcją. Nawet jeśli utworzę instancję Calculus i przekażę ją jako argument do funkcji, która z kolei wygeneruje obiekty Expr, to powrót funkcji nie zostanie rozpoznany jako obiekt tego samego typu, który został utworzony poza funkcją.

Może mój angielski nie jest wystarczająco jasny, pozwól mi spróbować zabawnego przykładu tego, co chciałbym zrobić (nie rzeczywistego generatora ScalaCheck, ale wystarczająco blisko).

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

Teraz, gdy próbuję skompilować powyższy kod otrzymuję wiele

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

I to samo się dzieje, gdy próbuję zrobić coś takiego:

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

Należy pamiętać, że sam generator nie jest niepokojący, ale będę musiał zrobić podobne rzeczy (np. tworzyć i manipulować wyrażeniami instancji obliczeniowej) wiele na pozostałej części systemu.

Czy robię coś nie tak? Czy można robić to, co chcę?

Pomoc w tej sprawie jest bardzo potrzebna i doceniana. Z góry dziękuję.


Po otrzymaniu odpowiedzi od Apocalisp i wypróbowaniu jej.

Wielkie dzięki za odpowiedź, ale nadal istnieją pewne problemy. Proponowane rozwiązanie było zmienić podpis funkcji do:

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

zmieniłem podpis do wszystkich funkcji zaangażowanych: getRndExpr, getRndVal i getRndVar.I mam ten sam komunikat o błędzie wszędzie Wzywam tych funkcji i uzyskałem następujący komunikat o błędzie:

 
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) 

Ponieważ kompilator wydawało się być w stanie zorientować się odpowiednie typy zmieniłem wszystkie wywołania funkcji być jak poniżej:

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

Po tym, na pierwszych 2 wywołań funkcji (genRndVal i genRndVar) nie było kompilacji błąd, ale na 3 następujących połączeń (rekurencyjne Wezwania do genRndExpr), gdzie powrót funkcja ta jest wykorzystywana do budowania nowy obiekt Expr Mam następujący błąd:

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

Więc znowu utknąłem. Każda pomoc zostanie doceniona.

+0

Twój tytuł pytania jest nieco mylący. Zastanów się, jak "Jak oddzielić klasę wewnętrzną od eksponalnego w stosunku do zakresu klasy?" – Alexey

Odpowiedz

3

Problem polega na tym, że Scala nie jest w stanie zunifikować dwóch typów: Calculus[E]#Expr i Calculus[E]#Expr.

Te wyglądają tak samo, prawda? Weź pod uwagę, że możesz mieć dwa odrębne kamienie na pewnym typie E, każdy z własnym typem Expr. I nie chciałbyś mieszać wyrażeń tych dwóch.

Konieczne jest ograniczenie typów w taki sposób, aby typ zwracanego zwrotu był taki sam, jak typ Expr, jako wewnętrzny typ argumentu Calculus. Co trzeba zrobić, to w ten sposób:

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

Witam! Wielkie dzięki za odpowiedź, ale wciąż są pewne problemy. Próbowałem twojego rozwiązania. Zmieniłem sygnaturę dla wszystkich zaangażowanych funkcji: getRndExpr, getRndVal i getRndVar. I mam ten sam komunikat o błędzie wszędzie, gdzie nazywam te funkcje. Nie mogę opisać tutaj w pełni problemu z powodu braku miejsca, dlatego edytuję treść pytania, aby móc odpowiedzieć poprawnie. – Jeff

1

Jeśli nie chcą czerpać konkretny rachunek z rachunku potem po prostu przejść do wyr zakresie globalnym lub przekazać ją za pośrednictwem globalnej zakresie:

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

    ... the rest like in your code 
} 

this question odnosi się do dokładnie tego samego problemu.

Jeśli nie chcesz, aby podtypu rachunku i ponownie zdefiniować wyrażenie tam (co jest mało prawdopodobne), trzeba:

umieścić getRndExpr do klasy Rachunek lub umieścić getRndExpr na cechy pochodnych:

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

odnoszą się do wątku this z tego powodu.

Powiązane problemy