Как использовать объекты в качестве модулей/функторов в 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 {...}
}

Так, чтобы я мог создать другой экземпляр исчисления для каждой решетки (операции, которые я выполню, нуждаются в информации, которой максимальные и минимальные значения решетки). Я хочу смочь смешать выражения того же исчисления, но не быть позволенным смешать выражения различных.Пока все хорошо. Я могу создать свои экземпляры исчисления, но проблема состоит в том, что я не могу записать функции в других классах, которые управляют ими.

Например, я пытаюсь создать синтаксический анализатор, чтобы считать выражения из файла и возвратить их; я также пытался записать случайный генератор выражения для использования в моих тестах с ScalaCheck. Оказывается, что каждый раз функция генерирует объект Expr, что я не могу использовать ее вне функции. Даже если я создаю экземпляр Исчисления и передаю его как аргумент функции, которая в свою очередь генерирует объекты 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))

После этого на первых 2 вызовах функции (genRndVal и genRndVar) не было никакой ошибки компиляции, но на следующих 3 вызовах (рекурсивные вызовы genRndExpr), где возврат функции используется для создания нового объекта Expr, что я получил следующую ошибку:

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

Так, снова, я застреваю. Любая справка будет цениться.

6
задан Jeff 10 April 2010 в 07:43
поделиться

2 ответа

Проблема в том, что Scala не может объединить два типа Исчисление [E] #Expr и Исчисление [E] #Expr .

Они кажутся вам одинаковыми, верно? Хорошо, представьте, что у вас может быть два различных исчисления над некоторым типом E , каждое со своим собственным типом Expr . И вы не захотите смешивать выражения этих двух.

Вам необходимо ограничить типы таким образом, чтобы возвращаемый тип был тем же типом Expr , что и внутренний тип Expr вашего аргумента Calculus . Вам нужно сделать следующее:

def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
3
ответ дан 17 December 2019 в 07:02
поделиться

Если вы не хотите выводить конкретное исчисление из Calculus, то просто переместите Expr в глобальную область видимости или обратитесь к нему через глобальную область видимости:

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

    ... the rest like in your code
}

этот вопрос относится к точно такой же проблеме.

Если вы все-таки хотите сделать подтип Calculus и переопределить там Expr (что маловероятно), вам придется:

поместить getRndExpr в класс Calculus или поместить getRndExpr в производный признак:

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

обратитесь к этой теме, чтобы узнать, почему так.

1
ответ дан 17 December 2019 в 07:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: