Как сопоставить несколько классов case и извлечь одинаковые (именованные) аргументы?

Рассмотрим следующий надуманный пример реализации унарных и бинарных операций над выражениями вещественных чисел.

abstract class DoubleE
case class Negate(x: DoubleE) extends DoubleE
case class Reciprocal(x: DoubleE) extends DoubleE
case class Mult(lhs: DoubleE, rhs: DoubleE) extends DoubleE
case class Div(lhs: DoubleE, rhs: DoubleE) extends DoubleE
...
// a lot more binary operations

Теперь я хочу сделать что-то вроде:

(e: DoubleE) match {
   case DoubleEUnary(x) => ... // use x ...
   case DoubleEBinary(a, b) => ... // use a and b ...
}

а иногда мне даже хотелось бы сослаться на соответствующий тип; Например

e match {
  case DoubeEBinary(a, b) => DoubleEBinary(b, a)
  ...
}

Некоторые из неудачных попыток, которые я пробовал:

  • abstract case class DoubleEBinary(a: DoubleE, b: DoubleE) + расширить от этого, но это не разрешено: Ошибка: ... наследование от случая к случаю запрещено. Чтобы обойти это ограничение, используйте экстракторы для сопоставления с образцом на неконечных узлах
  • Намёк на ошибку выше:

    abstract case class DoubleEBinary(a: DoubleE, b: DoubleE)
        def unapply(binOp: DoubleEBinary) = Some((a, b))
    

    что тоже не работает: Ошибка: не найдено: значение DoubleEBinary

  • Trying to use case aliases
    • case binOp @ (Mult(a, b) | Div(a, b) | ...) => ...
    • case binOp(a, b) @ (Mult(_, _) | Div(_, _) | ...) => ...
    • case (binOp @ Mult(a, b)) | (binOp @ Div(a, b)) => ...

Одна вещь, которую я не пробовал, - это перегрузка вложенными функциями, что кажется излишним...

Есть ли хороший способ сопоставить несколько классов случаев в сценариях, подобных приведенным выше?

Примечание. Добавление дополнительных методов, классов, трейтов в наследование — это нормально.


person eold    schedule 21.09.2014    source источник
comment
Я удалил свой ответ, потому что он не решает вашу проблему, но обратите внимание, что конкретный класс необходим, потому что абстрактные классы не могут быть созданы, если вы хотите, вам нужно отказаться от модификатора abstract.   -  person Ende Neu    schedule 21.09.2014


Ответы (2)


Подсказка в сообщении об ошибке:

scala> object Binary { def unapply(e: Mult) = Mult.unapply(e) ; def unapply(e: Div) = Div.unapply(e) }
defined object Binary

scala> Div(null,null) match { case Binary(a,b) => (a,b) }
res3: (DoubleE, DoubleE) = (null,null)

Извините, я собираюсь спать здесь, это было бы лучше выразить как unapply(b: Binary) или как DoubleE.unapply(d: DoubleE), что соответствует подтипам.

person som-snytt    schedule 21.09.2014

Начиная со Scala 2.13, вероятно, задолго до этого, вы можете делать именно то, что набрали

person Randomness Slayer    schedule 26.01.2021