Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Organizing enumerations in Scala

sorry for the long winded question:

Say I have a list of animals and I want to split them up like so:

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

Now, these animals also have to live somewhere. So there's a

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

What's the best way to implement this in Scala? One attempt was:

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

and then

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
}

Sadly, this won't work. The Dog from Carnivore is different from the Dog in BasicAnimal. That is Carnivore.Dog == BasicAnimal.Dog returns false, so, the only way to reuse the code from BasicHouse in Den is by having a rather hacky equality method that compares the strings of the enums (or something similar). It works, but it's very unclean. Can you see any other possibilities?

like image 429
Henry Henrinson Avatar asked Sep 08 '11 12:09

Henry Henrinson


1 Answers

As per @paradigmatic's answer, but with a few enhancements:

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

Making the traits abstract allows you to take advantage of completeness checking in pattern matches (which would otherwise warn that a match on the trait itself wasn't attempted)

The Omnivore traits removes some duplication, and also helps with pattern matching

Using extends instead of self typing is just plain cleaner, and more intuitive

case object instead of object is mostly done to better document the intent of the code, and also to provide a sane implementation of toString

like image 52
Kevin Wright Avatar answered Oct 13 '22 20:10

Kevin Wright