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 another instance of calculus for each lattice (the operations that I will perform are necessary, information about which is the maximum and minimum values โโof the lattice). I want to be able to mix expressions of the same calculus, but not to mix different expressions. So far, so good. I can create my own calculus instances, but the problem is that I cannot write functions in other classes that manipulate them.
For example, I'm trying to create a parser to read expressions from a file and return them; I also tried to write a random expression generator for use in my tests with ScalaCheck. It turns out that every time a function generates an Expr object, I cannot use it outside the function. Even if I create an instance of Calculus and pass it as an argument to the function, which in turn will generate Expr objects, the return of the function will not be recognized as the same type of objects created outside the function.
Perhaps my English is not clear enough, let me try a toy example of what I would like to do (not a 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 a lot
error: type mismatch;
found: plg.mvfml.Calculus [E] #Expr
required: c.Expr
case 0 => new c.Neg (genRndExpr (c, level + 1))
And the same thing happens if I try to do something like:
val boolCalc = new Calculus(Bool) val e1: boolCalc.Expr = genRndExpr(boolCalc)
Note that the generator itself is not a concern, but I will need to do such things (i.e. create and process expressions of a calculus instance) a lot in the rest of the system.
Am I doing something wrong? Can I do what I want to do?
Help on this issue is very necessary and appreciated. Thank you very much in advance.
After receiving the response from Apocalisp and try it.
Thanks a lot for the answer, but there are still some problems. The proposed solution was to change the function signature to:
def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
I changed the signature for all functions involved: getRndExpr, getRndVal and getRndVar. And I received the same error message wherever I call these functions, and received the following error message:
error: inferred type arguments [Nothing, C] do not conform to method genRndVar
type parameter bounds [E, C <: plg.mvfml.Calculus [E]]
case 0 => genRndVar (c)
Since the compiler did not seem to be able to determine the correct types, I changed all the function calls as below:
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
After that, the first 2 calls to the function (genRndVal and genRndVar) did not compile, and the next 3 calls (recursive calls to genRndExpr), where the function return is used to build a new Expr object, I received 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 would be appreciated.