Are there any best-practice guidelines on when to use case classes (or case objects) vs extending Enumeration in Scala?
They seem to offer some of the same benefits.
One big difference is that Enumeration
s 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 object
s. 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 typeCurrency
can now sneak into other parts of an API.It's advisable to push that case outside
Enumeration
and make the client deal with anOption[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 object
s over Enumeration
s are:
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.
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.