2010-04-09 11 views
6

voglio usare istanze di oggetti come moduli/funtori, più o meno come illustrato di seguito:Come utilizzare gli oggetti come moduli/funtori 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 {...} 
} 

In modo che posso creare un'istanza diversa calcolo per ogni reticolo (le operazioni che si esibiranno bisogno delle informazioni di cui sono i valori massimi e minimi del reticolo). Voglio essere in grado di mescolare espressioni dello stesso calcolo, ma non mi è permesso mescolare espressioni di tipi diversi. Fin qui tutto bene. Posso creare le mie istanze di calcolo, ma il problema è che non posso scrivere funzioni in altre classi che le manipolano.

Ad esempio, sto cercando di creare un parser per leggere le espressioni da un file e restituirle; Stavo anche cercando di scrivere un generatore di espressioni casuali da utilizzare nei miei test con ScalaCheck. Risulta che ogni volta che una funzione genera un oggetto Expr non posso usarlo al di fuori della funzione. Anche se creo l'istanza di Calcolo e la passiamo come argomento alla funzione che a sua volta genererà gli oggetti Expr, il ritorno della funzione non viene riconosciuto come dello stesso tipo degli oggetti creati al di fuori della funzione.

Forse il mio inglese non è abbastanza chiaro, permettetemi di provare un esempio di quello che vorrei fare (non il vero generatore di ScalaCheck, ma abbastanza vicino).

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

Ora, se provo a compilare il codice di cui sopra ricevo un sacco di

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

E lo stesso accade se provo a fare qualcosa di simile:

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

prega di notare che il generatore stesso non è preoccupante, ma dovrò fare cose simili (ad es. creare e manipolare espressioni di istanze di calcolo) molto sul resto del sistema.

Sto facendo qualcosa di sbagliato? È possibile fare ciò che voglio fare?

L'assistenza su questo argomento è altamente necessaria e apprezzata. Grazie mille in anticipo.


Dopo aver ricevuto risposta da Apocalisp e averlo provato.

Grazie mille per la risposta, ma ci sono ancora alcuni problemi. La soluzione proposta era quello di cambiare la firma della funzione da:

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

Ho cambiato la firma per tutte le funzioni coinvolte: getRndExpr, getRndVal e getRndVar.E ho ottenuto lo stesso messaggio di errore in tutto il mondo che io chiamo queste funzioni e ottenuto il seguente messaggio di errore:

 
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) 

Dal momento che il compilatore sembrava essere in grado di capire i tipi giusti ho cambiato tutti i chiamata di funzione per essere come di seguito:

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

Dopo questo, sui primi 2 chiamate di funzione (genRndVal e genRndVar) non ci sono stati errori di compilazione, ma sui seguenti 3 chiamate (chiamate ricorsive a genRndExpr), dove il ritorno della funzione viene utilizzata per costruire un nuovo oggetto Expr Ho ricevuto il seguente errore:

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

Quindi, di nuovo, sono bloccato. Qualsiasi aiuto sarà apprezzato.

+0

Il titolo della domanda è un po 'fuorviante. Prendi in considerazione qualcosa come "Come fare riferimento alle classi interne da expternal relativamente allo scope di classe?" – Alexey

risposta

3

Il problema è che Scala non è in grado di unificare i due tipi Calculus[E]#Expr e Calculus[E]#Expr.

Questi ti sembrano uguali, giusto? Bene, considera che potresti avere due calcoli distinti su un tipo E, ognuno con il proprio tipo Expr. E tu non vorresti mescolare le espressioni dei due.

È necessario vincolare i tipi in modo che il tipo restituito sia lo stesso tipo Expr come il tipo interno Expr dell'argomento Calculus. Quello che devi fare è questo:

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

Ciao! Grazie mille per la risposta, ma ci sono ancora alcuni problemi. Ho provato la soluzione. Ho cambiato la firma per tutte le funzioni coinvolte: getRndExpr, getRndVal e getRndVar. E ho ricevuto lo stesso messaggio di errore ovunque chiamo queste funzioni. Non riesco a descrivere completamente il problema qui a causa della mancanza di spazio, quindi modifico il corpo della domanda per poter rispondere correttamente. – Jeff

1

Se non si desidera derivare un calcolo specifico dal Calculus poi basta passare Espr di portata globale o fare riferimento attraverso portata globale:

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

    ... the rest like in your code 
} 

this question si riferisce esattamente allo stesso problema.

Se si vuole fare un sottotipo di calcolo e ridefinire Expr là (quello che è improbabile), è necessario:

messo getRndExpr nella classe Calcolo o mettere getRndExpr in un tratto derivato:

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

fare riferimento al thread this per il motivo per cui così.

Problemi correlati