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