Case objects vs Enumerations in Scala

One big difference is that Enumerations come with support for instantiating them from some name String. For example:

object Currency extends Enumeration {
   val GBP = Value("GBP")
   val EUR = Value("EUR") //etc.
} 

Then you can do:

val ccy = Currency.withName("EUR")

This is useful when wishing to persist enumerations (for example, to a database) or create them from data residing in files. However, I find in general that enumerations are a bit clumsy in Scala and have the feel of an awkward add-on, so I now tend to use case objects. A case object is more flexible than an enum:

sealed trait Currency { def name: String }
case object EUR extends Currency { val name = "EUR" } //etc.

case class UnknownCurrency(name: String) extends Currency

So now I have the advantage of…

trade.ccy match {
  case EUR                   =>
  case UnknownCurrency(code) =>
}

As @chaotic3quilibrium pointed out (with some corrections to ease reading):

Regarding “UnknownCurrency(code)” pattern, there are other ways to handle not finding a currency code string than “breaking” the closed set nature of the Currency type. UnknownCurrency being of type Currency can now sneak into other parts of an API.

It’s advisable to push that case outside Enumeration and make the client deal with an Option[Currency] type that would clearly indicate there is really a matching problem and “encourage” the user of the API to sort it out him/herself.

To follow up on the other answers here, the main drawbacks of case objects over Enumerations are:

  1. Can’t iterate over all instances of the “enumeration”. This is certainly the case, but I’ve found it extremely rare in practice that this is required.

  2. Can’t instantiate easily from persisted value. This is also true but, except in the case of huge enumerations (for example, all currencies), this doesn’t present a huge overhead.

Leave a Comment

tech