Monday 18 July 2011

(Almost) A way to extend existing Enumerations in Scala

I've ran into this problem a while ago and it took a bit of thinking before I came to a solution.

Say you have an enumeration:

object BasicAnimal extends Enumeration{
    val Cat, Dog = Value
}

and a function:

def noise(animal:BasicAnimal) = println("It's an animal!")

Say you want to extend the enumeration to include other animals as well:

object FourLeggedAnimal extends BasicAnimal{
    val Dragon = Value
}

So then Cat, Dog and Dragon would all be FourLeggedAnimal, yet only Dog and Cat will be BasicAnimal. Sadly, this wouldn't work as BasicAnimal is an object, not a class or a trait. So what's a guy to do? Try this for a size:

abstract class BasicAnimalAbstract extends Enumeration{
    type BasicAnimal = Value
    val Cat, Dog = Value
}
 
abstract class FourLeggedAnimalAbstract extends BasicAnimalAbstract{
    type FourLeggedAnimal = Value
    val Dragon = Value
}
 
object BasicAnimal extends BasicAnimalAbstract{}
object FourLeggedAnimal extends FourLeggedAnimalAbstract{}
 
 
def f(animal: FourLeggedAnimal) = println("It's an animal!")
 

Then you get:
 
    scala> f(Dragon)
    It's an animal!
    
    scala> f(Cat)
    It's an animal!
 
So it would seem that this is the solution. Sadly, this is only a partial solution. One of the issues is that
BasicAnimal.Cat != FourLeggedAnimal.Cat.
Moreover, not even casting would work!
FourLeggedAnimal.Cat.asInstanceOf[BasicAnimal] != BasicAnimal.Cat
 
So this only works to a certain extent. If you're desperate, you can do this in order to get equality
between elements of BasicAnimal and FourLeggedAnimal:

def findCorrespondent(animal: FourLeggedAnimal){
     BasicAnimal.withName(animal.toString)
}
 
Not ideal, but works. 

No comments:

Post a Comment