2010-04-09 10 views
6

Quiero usar instancias de objetos como módulos/funtores, más o menos como se muestra a continuación:¿Cómo usar objetos como módulos/functors en 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 {...} 
} 

De modo que pueda crear una instancia de cálculo diferente para cada celosía (las operaciones haré en necesitan la información de los cuales son los valores máximo y mínimo del enrejado). Quiero ser capaz de mezclar expresiones del mismo cálculo pero no permitir mezclar expresiones de diferentes. Hasta aquí todo bien. Puedo crear mis instancias de cálculo, pero el problema es que no puedo escribir funciones en otras clases que las manipulen.

Por ejemplo, estoy tratando de crear un analizador para leer expresiones de un archivo y devolverlas; También estaba tratando de escribir un generador de expresiones aleatorias para usar en mis pruebas con ScalaCheck. Resulta que cada vez que una función genera un objeto Expr no puedo usarlo fuera de la función. Incluso si creo la instancia de Cálculo y la paso como un argumento a la función que a su vez generará los objetos de Expr, el retorno de la función no se reconoce como del mismo tipo de los objetos creados fuera de la función.

Quizás mi inglés no sea lo suficientemente claro, permítanme probar un ejemplo de lo que me gustaría hacer (no el verdadero generador de ScalaCheck, pero lo suficientemente cerca).

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

Ahora, si intento compilar el código anterior recibo muchos

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

Y lo mismo ocurre si se intenta hacer algo como:

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

Tenga en cuenta que el generador en sí no es motivo de preocupación, pero tendré que hacer cosas similares (es decir, crear y manipular expresiones de instancias de cálculo) mucho en el resto del sistema.

¿Estoy haciendo algo mal? ¿Es posible hacer lo que quiero hacer?

La ayuda en esta materia es muy necesaria y apreciada. Muchas gracias por adelantado.


Después de recibir una respuesta de Apocalisp y probarlo.

Muchas gracias por la respuesta, pero todavía hay algunos problemas. La solución propuesta fue cambiar la firma de la función a:

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

me cambió la firma de todas las funciones involucradas: getRndExpr, getRndVal y getRndVar.Y me dieron el mismo mensaje de error en todas partes Yo llamo a estas funciones y obtuve el siguiente mensaje de error:

 
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) 

Desde el compilador parecía ser incapaz de averiguar el tipo correcto Me cambió todo llamada a la función de ser como a continuación:

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

Después de esto, en las 2 primeras llamadas de función (genRndVal y genRndVar) no hubo error de compilación, pero en las 3 llamadas siguientes (llamadas recursivas a genRndExpr), donde se utiliza el retorno de la función de construir una nuevo objeto Expr Obtuve el siguiente error:

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

Así que, de nuevo, estoy atascado. Cualquier ayuda será apreciada.

+0

El título de su pregunta es un poco engañoso. Considere algo así como "¿Cómo hacer referencia a las clases internas desde expternal con respecto al alcance de la clase?" – Alexey

Respuesta

3

El problema es que Scala no puede unificar los dos tipos Calculus[E]#Expr y Calculus[E]#Expr.

Aunque te parezcan iguales, ¿no? Bien, considere que podría tener dos cálculos distintos sobre algún tipo E, cada uno con su propio tipo Expr. Y no querrás mezclar expresiones de los dos.

Debe restringir los tipos de tal forma que el tipo de devolución sea del mismo tipo Expr que el tipo interno Expr de su argumento Calculus. Lo que tienes que hacer es lo siguiente:

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

¡Hola! Muchas gracias por la respuesta, pero todavía hay algunos problemas. Probé tu solución. Cambié la firma para todas las funciones involucradas: getRndExpr, getRndVal y getRndVar. Y recibí el mismo mensaje de error en todas partes a las que llamo estas funciones. No puedo describir completamente el problema aquí debido a la falta de espacio, por lo que editaré el cuerpo de la pregunta para poder responder correctamente. – Jeff

1

Si no desea derivar un cálculo específico del cálculo a continuación, sólo se mueven a Expr alcance global o remitirla a través de ámbito global:

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

    ... the rest like in your code 
} 

this question se refiere exactamente al mismo problema

Si desea hacer un subtipo de cálculo y redefinir Expr allí (lo que es poco probable), usted tiene que:

poner getRndExpr en la clase de Cálculo o poner getRndExpr en un rasgo derivado:

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

consulte this hilo por la razón de por qué.

Cuestiones relacionadas