Hi.
I want to use object instances as modules/functors, more or less as shown below:
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 {...}
}
So that I can create a different calculus instance for each lattice (the operations I will perform need the information of which are the maximum and minimum values of the lattice). I want to be able to mix expressions of the same calculus but not be allowed to mix expressions of different ones. So far, so good. I can create my calculus instances, but problem is that I can not write functions in other classes that manipulate them.
For example, I am trying to create a parser to read expressions from a file and return them; I also was trying to write an random expression generator to use in my tests with ScalaCheck. Turns out that every time a function generates an Expr object I can't use it outside the function. Even if I create the Calculus instance and pass it as an argument to the function that will in turn generate the Expr objects, the return of the function is not recognized as being of the same type of the objects created outside the function.
Maybe my english is not clear enough, let me try a toy example of what I would like to do (not the real ScalaCheck generator, but close enough).
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))
}
}
}
Now, if I try to compile the above code I get lots of
error: type mismatch; found : plg.mvfml.Calculus[E]#Expr required: c.Expr case 0 => new c.Neg(genRndExpr(c, level+1))
And the same happens if I try to do something like:
val boolCalc = new Calculus(Bool)
val e1: boolCalc.Expr = genRndExpr(boolCalc)
Please note that the generator itself is not of concern, but I will need to do similar things (i.e. create and manipulate calculus instance expressions) a lot on the rest of the system.
Am I doing something wrong? Is it possible to do what I want to do?
Help on this matter is highly needed and appreciated. Thanks a lot in advance.
After receiving an answer from Apocalisp and trying it.
Thanks a lot for the answer, but there are still some issues. The proposed solution was to change the signature of the function to:
def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
I changed the signature for all the functions involved: getRndExpr, getRndVal and getRndVar. And I got the same error message everywhere I call these functions and got the following error message:
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)
Since the compiler seemed to be unable to figure out the right types I changed all function call to be like below:
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
After this, on the first 2 function calls (genRndVal and genRndVar) there were no compiling error, but on the following 3 calls (recursive calls to genRndExpr), where the return of the function is used to build a new Expr object I got the following error:
error: type mismatch; found : C#Expr required: c.Expr case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
So, again, I'm stuck. Any help will be appreciated.