views:

968

answers:

5

I was wondering if there are any best practice guidelines on when to use case classes vs extending Enumeration in Scala. They seem to offer some of the same benefits.

+3  A: 

The advantages of using case classes over Enumerations are:

  • When using sealed case classes, the Scala compiler can tell if the match is fully specified e.g. when all possible matches are espoused in the matching declaration. With enumerations, the Scala compiler cannot tell.
  • Case classes naturally supports more fields than a Value based Enumeration which supports a name and ID.

The advantages of using Enumerations instead of case classes are:

  • Enumerations will generally be a bit less code to write.
  • Enumerations are a bit easier to understand for someone new to Scala since they are prevalent in other languages

So in general, if you just need a list of simple constants by name, use enumerations. Otherwise, if you need something a bit more complex or want the extra safety of the compiler telling you if you have all matches specified, use case classes.

Aaron
+8  A: 

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

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

Then you can do:

val ccy = Currency("EUR")

This is useful when wishing to persist enumerations (e.g. 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 UnknownCcy(code) =>
}

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 (e.g all currencies), this doesn't present a huge overhead

oxbow_lakes
Please take a notice of this... http://stackoverflow.com/questions/1898932/case-classes-vs-enumerations-in-scala/2257092#2257092
missingfaktor
+3  A: 

Another disadvantage of case classes versus Enumerations when you will need to iterate or filter across all instances. This is a built-in capability of Enumeration (and Java enums as well) while case classes don't automatically support such capability.

Just to make sure I understand, you mean there's no easy way to get a list of the total set of enumerated values with case classes?
Alex Miller
Yes - that's what is meant here
oxbow_lakes
A: 

@oxbow_lakes:

Sorry, but

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

simply doesn't work...

scala> val ccy = Currency("EUR")
<console>:5: error: type mismatch;
 found   : java.lang.String("EUR")
 required: Int
       val ccy = Currency("EUR")
                      ^

Could somebody provide a working solution allowing construciton from String?


EDIT:

the above mentioned error is reproducible under 2.8 and was first spotted on 2.7.7 (not sure, though). To reproduce it, ommit the 'public' "keyword", as in Scala it's not a keyword :)

Corrected version, as asked by me ;) :

abstract class OxbowsEnumeration extends Enumeration {
  def apply(x: String) = withName(x)
}

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

val ccy = Currency("EUR")
Artur Gajowy
What version of scala are you using? The example I gave above works just fine and is in production code
oxbow_lakes
A: 

Instantiate like this:

val ccy = Currency.EUR
randomnerd