Я хочу использовать экземпляры объектов в качестве модулей / функторов, более или менее как показано ниже:
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 {...}
}
Так что я могу создать разные экземпляры исчисления для каждой решетки (для операций, которые я буду выполнять, требуется информация о максимальных и минимальных значениях решетки). Я хочу иметь возможность смешивать выражения одного и того же исчисления, но не позволять смешивать выражения разных. Все идет нормально. Я могу создавать экземпляры исчисления, но проблема в том, что я не могу писать функции в других классах, которые ими манипулируют.
Например, я пытаюсь создать парсер для чтения выражений из файла и их возврата; Я также пытался написать генератор случайных выражений для использования в моих тестах с ScalaCheck. Оказывается, что каждый раз, когда функция генерирует объект Expr, я не могу использовать его вне функции. Даже если я создам экземпляр Calculus и передам его в качестве аргумента функции, которая, в свою очередь, сгенерирует объекты Expr, возвращаемая функция не распознается как объект того же типа, что и объекты, созданные вне функции.
Может быть, мой английский недостаточно ясен, позвольте мне попробовать пример того, что я хотел бы сделать (не настоящий генератор ScalaCheck, но достаточно близко).
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))
}
}
}
Теперь, если я попытаюсь скомпилировать приведенный выше код, я получу много
error: type mismatch;
found : plg.mvfml.Calculus[E]#Expr
required: c.Expr
case 0 => new c.Neg(genRndExpr(c, level+1))
И то же самое происходит, если я пытаюсь сделать что-то вроде:
val boolCalc = new Calculus(Bool)
val e1: boolCalc.Expr = genRndExpr(boolCalc)
Обратите внимание, что сам генератор не имеет значения, но мне нужно будет делать подобные вещи (то есть создавать и манипулировать выражениями экземпляров исчисления) во всей остальной системе.
Я что-то не так делаю?
Можно ли делать то, что я хочу делать?
Помощь по этому вопросу крайне необходима и ценится. Заранее большое спасибо.
Получив ответ от Apocalisp и попробовав его.
Большое спасибо за ответ, но все еще есть некоторые проблемы. Предложенное решение состояло в том, чтобы изменить сигнатуру функции на:
def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
Я изменил подпись для всех задействованных функций: getRndExpr, getRndVal и getRndVar. И я получаю одно и то же сообщение об ошибке везде, где я вызываю эти функции, и получаю следующее сообщение об ошибке:
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)
Поскольку компилятор, похоже, не мог определить правильные типы, я изменил вызов всех функций, как показано ниже:
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
После этого в первых двух вызовах функций (genRndVal и genRndVar) не было ошибок компиляции, но в следующих трех вызовах (рекурсивные вызовы genRndExpr), где возврат функции используется для создания нового объекта Expr Я получил следующую ошибку:
error: type mismatch;
found : C#Expr
required: c.Expr
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
Итак, я опять застрял. Любая помощь будет оценена.