Организация перечислений в Scala - PullRequest
9 голосов
/ 08 сентября 2011

извините за длинный вопрос:

Скажем, у меня есть список животных, и я хочу разделить их так:

BasicAnimal = {Cat, Dog}
Carnivore = {Cat, Dog, Dragon}
Herbivore = {Cat, Dog, Horse}

Теперь эти животные тоже должны где-то жить. Так что есть

BasicShelter with a method shelter(animal: BasicAnimal)
Den with a method shelter(animal: Carnivore)
Shed with a method shelter(animal: Herbivore)

Какой лучший способ реализовать это в Scala? Одна попытка была:

class BasicAnimal extends Enumeration{
   val Cat, Dog = Value
}
class Carnivore extends BasicAnimal{
   val Dragon = Value
}
class Herbivore extends BasicAnimal{
   val Horse = Value
}

, а затем

class BasicHouse{
  def shelter(animal: BasicAnimal) = {//lots of code}
}
class Den{
  def shelter(animal: Carnivore) = {
  //again lots of code, but the cases dealing with Cat and Dog can be relegated to super.shelter
  }
}
class Shed{
  //the same
}

К сожалению, это не сработает. Собака из Плотоядного отличается от Собаки в BasicAnimal. То есть Carnivore.Dog == BasicAnimal.Dog возвращает false, поэтому единственный способ повторно использовать код из BasicHouse в Den - использовать довольно хакерский метод равенства, который сравнивает строки перечислений (или что-то подобное). Это работает, но это очень нечисто. Можете ли вы увидеть какие-либо другие возможности?

Ответы [ 2 ]

9 голосов
/ 08 сентября 2011

В соответствии с ответом @ paradigmatic, но с некоторыми улучшениями:

sealed abstract trait BasicAnimal
sealed abstract trait Carnivore extends BasicAnimal
sealed abstract trait Herbivore extends BasicAnimal
sealed abstract trait Omnivore extends Carnivore with Herbivore

case object Dog extends Omnivore
case object Cat extends Omnivore
case object Dragon extends Carnivore
case object Horse extends Herbivore

Создание абстрактных признаков позволяет вам воспользоваться преимуществами проверки полноты в совпадениях с образцом (которая в противном случае предупредила бы о совпадении по признаку)сама по себе не была предпринята попытка)

Черты Omnivore устраняют некоторые дубликаты, а также помогают с сопоставлением с шаблоном

Использование extends вместо самостоятельного набора текста просто чище и интуитивно понятнее

case object вместо object в основном делается для лучшего документирования намерений кода, а также для обеспечения разумной реализации toString

5 голосов
/ 08 сентября 2011

Альтернативным решением для перечисления является использование sealed trait для определения перечислений.

На вашем примере:

sealed trait BasicAnimal
sealed trait Carnivore { self: BasicAnimal => }
sealed trait Herbivore { self: BasicAnimal => }

object Dog extends BasicAnimal with Carnivore with Herbivore
object Cat extends BasicAnimal with Carnivore with Herbivore
object Dragon extends BasicAnimal with Carnivore
object Horse extends BasicAnimal with Herbivore

Это более гибко, чем перечисление, но вы теряете возможность простого перечисления всех значений.

...