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 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:
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.
UPDATE:
A new macro based solution has been created which is far superior to the solution I outline below. I strongly recommend using this new macro based solution. And it appears plans for Dotty will make this style of enum solution part of the language. Whoohoo!
Summary:
There are three basic patterns for attempting to reproduce the Java Enum within a Scala project. Two of the three patterns; directly using Java Enum and scala.Enumeration, are not capable of enabling Scala's exhaustive pattern matching. And the third one; "sealed trait + case object", does...but has JVM class/object initialization complications resulting in inconsistent ordinal index generation.
I have created a solution with two classes; Enumeration and EnumerationDecorated, located in this Gist. I didn't post the code into this thread as the file for Enumeration was quite large (+400 lines - contains lots of comments explaining implementation context).
Details:
The question you're asking is pretty general; "...when to use caseclassesobjects vs extending [scala.]Enumeration". And it turns out there are MANY possible answers, each answer depending on the subtleties of the specific project requirements you have. The answer can be reduced down to three basic patterns.
To start, let's make sure we are working from the same basic idea of what an enumeration is. Let's define an enumeration mostly in terms of the Enum provided as of Java 5 (1.5):
It contains a naturally ordered closed set of named members
There is a fixed number of members
Members are naturally ordered and explicitly indexed
As opposed to being sorted based on some inate member queriable criteria
Each member has a unique name within the total set of all members
All members can easily be iterated through based on their indexes
A member can be retrieved with its (case sensitive) name
It would be quite nice if a member could also be retrieved with its case insensitive name
A member can be retrieved with its index
Members may easily, transparently and efficiently use serialization
Members may be easily extended to hold additional associated singleton-ness data
Thinking beyond Java's Enum, it would be nice to be able to explicitly leverage Scala's pattern matching exhaustiveness checking for an enumeration
Next, let's look at boiled down versions of the three most common solution patterns posted:
A) Actually directly using Java Enum pattern (in a mixed Scala/Java project):
public enum ChessPiece {
KING('K', 0)
, QUEEN('Q', 9)
, BISHOP('B', 3)
, KNIGHT('N', 3)
, ROOK('R', 5)
, PAWN('P', 1)
;
private char character;
private int pointValue;
private ChessPiece(char character, int pointValue) {
this.character = character;
this.pointValue = pointValue;
}
public int getCharacter() {
return character;
}
public int getPointValue() {
return pointValue;
}
}
The following items from the enumeration definition are not available:
3.1 - It would be quite nice if a member could also be retrieved with its case insensitive name
7 - Thinking beyond Java's Enum, it would be nice to be able to explicitly leverage Scala's pattern matching exhaustiveness checking for an enumeration
For my current projects, I don't have the benefit of taking the risks around the Scala/Java mixed project pathway. And even if I could choose to do a mixed project, item 7 is critical for allowing me to catch compile time issues if/when I either add/remove enumeration members, or am writing some new code to deal with existing enumeration members.
B) Using the "sealed trait + case objects" pattern:
sealed trait ChessPiece {def character: Char; def pointValue: Int}
object ChessPiece {
case object KING extends ChessPiece {val character = 'K'; val pointValue = 0}
case object QUEEN extends ChessPiece {val character = 'Q'; val pointValue = 9}
case object BISHOP extends ChessPiece {val character = 'B'; val pointValue = 3}
case object KNIGHT extends ChessPiece {val character = 'N'; val pointValue = 3}
case object ROOK extends ChessPiece {val character = 'R'; val pointValue = 5}
case object PAWN extends ChessPiece {val character = 'P'; val pointValue = 1}
}
The following items from the enumeration definition are not available:
1.2 - Members are naturally ordered and explicitly indexed
2 - All members can easily be iterated through based on their indexes
3 - A member can be retrieved with its (case sensitive) name
3.1 - It would be quite nice if a member could also be retrieved with its case insensitive name
4 - A member can be retrieved with its index
It's arguable it really meets enumeration definition items 5 and 6. For 5, it's a stretch to claim it's efficient. For 6, it's not really easy to extend to hold additional associated singleton-ness data.
C) Using the scala.Enumeration pattern (inspired by this StackOverflow answer):
object ChessPiece extends Enumeration {
val KING = ChessPieceVal('K', 0)
val QUEEN = ChessPieceVal('Q', 9)
val BISHOP = ChessPieceVal('B', 3)
val KNIGHT = ChessPieceVal('N', 3)
val ROOK = ChessPieceVal('R', 5)
val PAWN = ChessPieceVal('P', 1)
protected case class ChessPieceVal(character: Char, pointValue: Int) extends super.Val()
implicit def convert(value: Value) = value.asInstanceOf[ChessPieceVal]
}
The following items from the enumeration definition are not available (happens to be identical to the list for directly using the Java Enum):
3.1 - It would be quite nice if a member could also be retrieved with its case insensitive name
7 - Thinking beyond Java's Enum, it would be nice to be able to explicitly leverage Scala's pattern matching exhaustiveness checking for an enumeration
Again for my current projects, item 7 is critical for allowing me to catch compile time issues if/when I either add/remove enumeration members, or am writing some new code to deal with existing enumeration members.
So, given the above definition of an enumeration, none of the above three solutions work as they do not provide everything outlined in the enumeration definition above:
Java Enum directly in a mixed Scala/Java project
"sealed trait + case objects"
scala.Enumeration
Each of these solutions can be eventually reworked/expanded/refactored to attempt to cover some of each one's missing requirements. However, neither the Java Enum nor the scala.Enumeration solutions can be sufficiently expanded to provide item 7. And for my own projects, this is one of the more compelling values of using a closed type within Scala. I strongly prefer compile time warnings/errors to indicate I have a gap/issue in my code as opposed to having to glean it out of a production runtime exception/failure.
In that regard, I set about working with the case object pathway to see if I could produce a solution which covered all of the enumeration definition above. The first challenge was to push through the core of the JVM class/object initialization issue (covered in detail in this StackOverflow post). And I was finally able to figure out a solution.
As my solution is two traits; Enumeration and EnumerationDecorated, and since the Enumeration trait is over +400 lines long (lots of comments explaining context), I am forgoing pasting it into this thread (which would make it stretch down the page considerbly). For details, please jump directly to the Gist.
Here's what the solution ends up looking like using the same data idea as above (fully commented version available here) and implemented in EnumerationDecorated.
import scala.reflect.runtime.universe.{TypeTag,typeTag}
import org.public_domain.scala.utils.EnumerationDecorated
object ChessPiecesEnhancedDecorated extends EnumerationDecorated {
case object KING extends Member
case object QUEEN extends Member
case object BISHOP extends Member
case object KNIGHT extends Member
case object ROOK extends Member
case object PAWN extends Member
val decorationOrderedSet: List[Decoration] =
List(
Decoration(KING, 'K', 0)
, Decoration(QUEEN, 'Q', 9)
, Decoration(BISHOP, 'B', 3)
, Decoration(KNIGHT, 'N', 3)
, Decoration(ROOK, 'R', 5)
, Decoration(PAWN, 'P', 1)
)
final case class Decoration private[ChessPiecesEnhancedDecorated] (member: Member, char: Char, pointValue: Int) extends DecorationBase {
val description: String = member.name.toLowerCase.capitalize
}
override def typeTagMember: TypeTag[_] = typeTag[Member]
sealed trait Member extends MemberDecorated
}
This is an example usage of a new pair of enumeration traits I created (located in this Gist) to implement all of the capabilities desired and outlined in the enumeration definition.
One concern expressed is that the enumeration member names must be repeated (decorationOrderedSet in the example above). While I did minimize it down to a single repetition, I couldn't see how to make it even less due to two issues:
JVM object/class initialization for this particular object/case object model is undefined (see this Stackoverflow thread)
The content returned from the method getClass.getDeclaredClasses has an undefined order (and it is quite unlikely to be in the same order as the case object declarations in the source code)
Given these two issues, I had to give up trying to generate an implied ordering and had to explicitly require the client define and declare it with some sort of ordered set notion. As the Scala collections do not have an insert ordered set implementation, the best I could do was use a List and then runtime check that it was truly a set. It's not how I would have preferred to have achieved this.
And given the design required this second list/set ordering val, given the ChessPiecesEnhancedDecorated example above, it was possible to add case object PAWN2 extends Member and then forget to add Decoration(PAWN2,'P2', 2) to decorationOrderedSet. So, there is a runtime check to verify that the list is not only a set, but contains ALL of the case objects which extend the sealed trait Member. That was a special form of reflection/macro hell to work through.
Please leave comments and/or feedback on the Gist.
Case objects already return their name for their toString methods, so passing it in separately is unnecessary. Here is a version similar to jho's (convenience methods omitted for brevity):
trait Enum[A] {
trait Value { self: A => }
val values: List[A]
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
val values = List(EUR, GBP)
}
Objects are lazy; by using vals instead we can drop the list but have to repeat the name:
trait Enum[A <: {def name: String}] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed abstract class Currency(val name: String) extends Currency.Value
object Currency extends Enum[Currency] {
val EUR = new Currency("EUR") {}
val GBP = new Currency("GBP") {}
}
If you don't mind some cheating, you can pre-load your enumeration values using the reflection API or something like Google Reflections. Non-lazy case objects give you the cleanest syntax:
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
Nice and clean, with all the advantages of case classes and Java enumerations. Personally, I define the enumeration values outside of the object to better match idiomatic Scala code:
object Currency extends Enum[Currency]
sealed trait Currency extends Currency.Value
case object EUR extends Currency
case object GBP extends Currency
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.
UPDATE: The code below has a bug, described here. The test program below works, but if you were to use DayOfWeek.Mon (for example) before DayOfWeek itself, it would fail because DayOfWeek has not been initialized (use of an inner object does not cause an outer object to be initialized). You can still use this code if you do something like val enums = Seq( DayOfWeek ) in your main class, forcing initialization of your enums, or you can use chaotic3quilibrium's modifications. Looking forward to a macro-based enum!
If you want
warnings about non-exhaustive pattern matches
an Int ID assigned to each enum value, which you can optionally control
an immutable List of the enum values, in the order they were defined
an immutable Map from name to enum value
an immutable Map from id to enum value
places to stick methods/data for all or particular enum values, or for the enum as a whole
ordered enum values (so you can test, for example, whether day < Wednesday)
the ability to extend one enum to create others
then the following may be of interest. Feedback welcome.
In this implementation there are abstract Enum and EnumVal base classes, which you extend. We'll see those classes in a minute, but first, here's how you would define an enum:
object DayOfWeek extends Enum {
sealed abstract class Val extends EnumVal
case object Mon extends Val; Mon()
case object Tue extends Val; Tue()
case object Wed extends Val; Wed()
case object Thu extends Val; Thu()
case object Fri extends Val; Fri()
case object Sat extends Val; Sat()
case object Sun extends Val; Sun()
}
Note that you have to use each enum value (call its apply method) to bring it to life. [I wish inner objects weren't lazy unless I specifically ask for them to be. I think.]
We could of course add methods/data to DayOfWeek, Val, or the individual case objects if we so desired.
And here's how you would use such an enum:
object DayOfWeekTest extends App {
// To get a map from Int id to enum:
println( DayOfWeek.valuesById )
// To get a map from String name to enum:
println( DayOfWeek.valuesByName )
// To iterate through a list of the enum values in definition order,
// which can be made different from ID order, and get their IDs and names:
DayOfWeek.values foreach { v => println( v.id + " = " + v ) }
// To sort by ID or name:
println( DayOfWeek.values.sorted mkString ", " )
println( DayOfWeek.values.sortBy(_.toString) mkString ", " )
// To look up enum values by name:
println( DayOfWeek("Tue") ) // Some[DayOfWeek.Val]
println( DayOfWeek("Xyz") ) // None
// To look up enum values by id:
println( DayOfWeek(3) ) // Some[DayOfWeek.Val]
println( DayOfWeek(9) ) // None
import DayOfWeek._
// To compare enums as ordinals:
println( Tue < Fri )
// Warnings about non-exhaustive pattern matches:
def aufDeutsch( day: DayOfWeek.Val ) = day match {
case Mon => "Montag"
case Tue => "Dienstag"
case Wed => "Mittwoch"
case Thu => "Donnerstag"
case Fri => "Freitag"
// Commenting these out causes compiler warning: "match is not exhaustive!"
// case Sat => "Samstag"
// case Sun => "Sonntag"
}
}
Here's what you get when you compile it:
DayOfWeekTest.scala:31: warning: match is not exhaustive!
missing combination Sat
missing combination Sun
def aufDeutsch( day: DayOfWeek.Val ) = day match {
^
one warning found
You can replace "day match" with "( day: #unchecked ) match" where you don't want such warnings, or simply include a catch-all case at the end.
When you run the above program, you get this output:
Map(0 -> Mon, 5 -> Sat, 1 -> Tue, 6 -> Sun, 2 -> Wed, 3 -> Thu, 4 -> Fri)
Map(Thu -> Thu, Sat -> Sat, Tue -> Tue, Sun -> Sun, Mon -> Mon, Wed -> Wed, Fri -> Fri)
0 = Mon
1 = Tue
2 = Wed
3 = Thu
4 = Fri
5 = Sat
6 = Sun
Mon, Tue, Wed, Thu, Fri, Sat, Sun
Fri, Mon, Sat, Sun, Thu, Tue, Wed
Some(Tue)
None
Some(Thu)
None
true
Note that since the List and Maps are immutable, you can easily remove elements to create subsets, without breaking the enum itself.
Here is the Enum class itself (and EnumVal within it):
abstract class Enum {
type Val <: EnumVal
protected var nextId: Int = 0
private var values_ = List[Val]()
private var valuesById_ = Map[Int ,Val]()
private var valuesByName_ = Map[String,Val]()
def values = values_
def valuesById = valuesById_
def valuesByName = valuesByName_
def apply( id : Int ) = valuesById .get(id ) // Some|None
def apply( name: String ) = valuesByName.get(name) // Some|None
// Base class for enum values; it registers the value with the Enum.
protected abstract class EnumVal extends Ordered[Val] {
val theVal = this.asInstanceOf[Val] // only extend EnumVal to Val
val id = nextId
def bumpId { nextId += 1 }
def compare( that:Val ) = this.id - that.id
def apply() {
if ( valuesById_.get(id) != None )
throw new Exception( "cannot init " + this + " enum value twice" )
bumpId
values_ ++= List(theVal)
valuesById_ += ( id -> theVal )
valuesByName_ += ( toString -> theVal )
}
}
}
And here is a more advanced use of it which controls the IDs and adds data/methods to the Val abstraction and to the enum itself:
object DayOfWeek extends Enum {
sealed abstract class Val( val isWeekday:Boolean = true ) extends EnumVal {
def isWeekend = !isWeekday
val abbrev = toString take 3
}
case object Monday extends Val; Monday()
case object Tuesday extends Val; Tuesday()
case object Wednesday extends Val; Wednesday()
case object Thursday extends Val; Thursday()
case object Friday extends Val; Friday()
nextId = -2
case object Saturday extends Val(false); Saturday()
case object Sunday extends Val(false); Sunday()
val (weekDays,weekendDays) = values partition (_.isWeekday)
}
I have a nice simple lib here that allows you to use sealed traits/classes as enum values without having to maintain your own list of values. It relies on a simple macro that is not dependent on the buggy knownDirectSubclasses.
https://github.com/lloydmeta/enumeratum
Update March 2017: as commented by Anthony Accioly, the scala.Enumeration/enum PR has been closed.
Dotty (next generation compiler for Scala) will take the lead, though dotty issue 1970 and Martin Odersky's PR 1958.
Note: there is now (August 2016, 6+ years later) a proposal to remove scala.Enumeration: PR 5352
Deprecate scala.Enumeration, add #enum annotation
The syntax
#enum
class Toggle {
ON
OFF
}
is a possible implementation example, intention is to also support ADTs that conform to certain restrictions (no nesting, recursion or varying constructor parameters), e. g.:
#enum
sealed trait Toggle
case object ON extends Toggle
case object OFF extends Toggle
Deprecates the unmitigated disaster that is scala.Enumeration.
Advantages of #enum over scala.Enumeration:
Actually works
Java interop
No erasure issues
No confusing mini-DSL to learn when defining enumerations
Disadvantages: None.
This addresses the issue of not being able to have one codebase that
supports Scala-JVM, Scala.js and Scala-Native (Java source code not supported on Scala.js/Scala-Native, Scala source code not able to define enums that are accepted by existing APIs on Scala-JVM).
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.
In other words: "there's no easy way to get a list of the total set of enumerated values with case classes".
If you are serious about maintaining interoperability with other JVM languages (e.g. Java) then the best option is to write Java enums. Those work transparently from both Scala and Java code, which is more than can be said for scala.Enumeration or case objects. Let's not have a new enumerations library for every new hobby project on GitHub, if it can be avoided!
I've seen various versions of making a case class mimic an enumeration. Here is my version:
trait CaseEnumValue {
def name:String
}
trait CaseEnum {
type V <: CaseEnumValue
def values:List[V]
def unapply(name:String):Option[String] = {
if (values.exists(_.name == name)) Some(name) else None
}
def unapply(value:V):String = {
return value.name
}
def apply(name:String):Option[V] = {
values.find(_.name == name)
}
}
Which allows you to construct case classes that look like the following:
abstract class Currency(override name:String) extends CaseEnumValue {
}
object Currency extends CaseEnum {
type V = Site
case object EUR extends Currency("EUR")
case object GBP extends Currency("GBP")
var values = List(EUR, GBP)
}
Maybe someone could come up with a better trick than simply adding a each case class to the list like I did. This was all I could come up with at the time.
I prefer case objects (it's a matter of personal preference). To cope with the problems inherent to that approach (parse string and iterate over all elements), I've added a few lines that are not perfect, but are effective.
I'm pasting you the code here expecting it could be useful, and also that others could improve it.
/**
* Enum for Genre. It contains the type, objects, elements set and parse method.
*
* This approach supports:
*
* - Pattern matching
* - Parse from name
* - Get all elements
*/
object Genre {
sealed trait Genre
case object MALE extends Genre
case object FEMALE extends Genre
val elements = Set (MALE, FEMALE) // You have to take care this set matches all objects
def apply (code: String) =
if (MALE.toString == code) MALE
else if (FEMALE.toString == code) FEMALE
else throw new IllegalArgumentException
}
/**
* Enum usage (and tests).
*/
object GenreTest extends App {
import Genre._
val m1 = MALE
val m2 = Genre ("MALE")
assert (m1 == m2)
assert (m1.toString == "MALE")
val f1 = FEMALE
val f2 = Genre ("FEMALE")
assert (f1 == f2)
assert (f1.toString == "FEMALE")
try {
Genre (null)
assert (false)
}
catch {
case e: IllegalArgumentException => assert (true)
}
try {
Genre ("male")
assert (false)
}
catch {
case e: IllegalArgumentException => assert (true)
}
Genre.elements.foreach { println }
}
I've been going back and forth on these two options the last few times I've needed them. Up until recently, my preference has been for the sealed trait/case object option.
1) Scala Enumeration Declaration
object OutboundMarketMakerEntryPointType extends Enumeration {
type OutboundMarketMakerEntryPointType = Value
val Alpha, Beta = Value
}
2) Sealed Traits + Case Objects
sealed trait OutboundMarketMakerEntryPointType
case object AlphaEntryPoint extends OutboundMarketMakerEntryPointType
case object BetaEntryPoint extends OutboundMarketMakerEntryPointType
While neither of these really meet all of what a java enumeration gives you, below are the pros and cons:
Scala Enumeration
Pros:
-Functions for instantiating with option or directly assuming accurate (easier when loading from a persistent store)
-Iteration over all possible values is supported
Cons:
-Compilation warning for non-exhaustive search is not supported (makes pattern matching less ideal)
Case Objects/Sealed traits
Pros:
-Using sealed traits, we can pre-instantiate some values while others can be injected at creation time
-full support for pattern matching (apply/unapply methods defined)
Cons:
-Instantiating from a persistent store - you often have to use pattern matching here or define your own list of all possible 'enum values'
What ultimately made me change my opinion was something like the following snippet:
object DbInstrumentQueries {
def instrumentExtractor(tableAlias: String = "s")(rs: ResultSet): Instrument = {
val symbol = rs.getString(tableAlias + ".name")
val quoteCurrency = rs.getString(tableAlias + ".quote_currency")
val fixRepresentation = rs.getString(tableAlias + ".fix_representation")
val pointsValue = rs.getInt(tableAlias + ".points_value")
val instrumentType = InstrumentType.fromString(rs.getString(tableAlias +".instrument_type"))
val productType = ProductType.fromString(rs.getString(tableAlias + ".product_type"))
Instrument(symbol, fixRepresentation, quoteCurrency, pointsValue, instrumentType, productType)
}
}
object InstrumentType {
def fromString(instrumentType: String): InstrumentType = Seq(CurrencyPair, Metal, CFD)
.find(_.toString == instrumentType).get
}
object ProductType {
def fromString(productType: String): ProductType = Seq(Commodity, Currency, Index)
.find(_.toString == productType).get
}
The .get calls were hideous - using enumeration instead I can simply call the withName method on the enumeration as follows:
object DbInstrumentQueries {
def instrumentExtractor(tableAlias: String = "s")(rs: ResultSet): Instrument = {
val symbol = rs.getString(tableAlias + ".name")
val quoteCurrency = rs.getString(tableAlias + ".quote_currency")
val fixRepresentation = rs.getString(tableAlias + ".fix_representation")
val pointsValue = rs.getInt(tableAlias + ".points_value")
val instrumentType = InstrumentType.withNameString(rs.getString(tableAlias + ".instrument_type"))
val productType = ProductType.withName(rs.getString(tableAlias + ".product_type"))
Instrument(symbol, fixRepresentation, quoteCurrency, pointsValue, instrumentType, productType)
}
}
So I think my preference going forward is to use Enumerations when the values are intended to be accessed from a repository and case objects/sealed traits otherwise.
For those still looking how to get GatesDa's answer to work:
You can just reference the case object after declaring it to instantiate it:
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency;
EUR //THIS IS ONLY CHANGE
case object GBP extends Currency; GBP //Inline looks better
}
I think the biggest advantage of having case classes over enumerations is that you can use type class pattern a.k.a ad-hoc polymorphysm. Don't need to match enums like:
someEnum match {
ENUMA => makeThis()
ENUMB => makeThat()
}
instead you'll have something like:
def someCode[SomeCaseClass](implicit val maker: Maker[SomeCaseClass]){
maker.make()
}
implicit val makerA = new Maker[CaseClassA]{
def make() = ...
}
implicit val makerB = new Maker[CaseClassB]{
def make() = ...
}
Related
what's a diff between these two snippets
object Main extends Enumeration {
val first : String = "Thriller"
val second : String = "Horror"
val third : String = "Comedy"
val fourth : String = "Romance"
// Main Method
def main(args: Array[String]) {
println(s" Movie Genres = ${Main.first}")
}
}
And
object Main extends Enumeration
{
type Main = Value
// Assigning values
val first = Value("Thriller")
val second = Value("Horror")
val third = Value("Comedy")
val fourth = Value("Romance")
// Main method
def main(args: Array[String])
{
println(s"Main Movie Genres = ${Main.values}")
}
}
what benefit am getting by using type Main = Value in second code snippet ? I'm struggling to understand this even after reading alot online. Please share any simple understanding if possible.
Because in Java when we define this below code, all values (HEARTS, DIAMOND etc) are of CardSuit type, so can't we say that (first, second etc) are of Main type ?
enum CardSuit{
HEARTS,
DIAMONDS,
CLUBS,
SPADES,
}
Last is, Is there any way I could have more than 2 attributes assign to any enum's constants in scala ? For example val first = Value("Thriller", "movie, "london", "production")
Enumeration used private constructors and other tricks to:
define type Value within your object that you extended with Enumeration
limited constructors so that they could only be called from within your object
make these constructors do side-effects so that each new value would append itself to the mutable list (which isn't mutable from outside)
This approach has several issues:
when you do object MyEnum extends Enumeration your enumerations will NOT be defined as MyEnum type, but as MyEnum.Value
if you want to use MyEnum as the name of enumeration, you have to use type alias type MyEnum = MyEnum.Value or type MyEnum = Value
however the type of companion will still be MyEnum.Value.type instead of MyEnum.type. Aliases creating MyEnum.MyEnum and MyEnum.MyEnum.type are just silly
each value is of the same type, so type level programming options are quite limited
you cannot customize Value: let it inherit your type, add some field, store something else than String, not store anything...
Points 1. and 2. are the reasons people did the type aliasing. All 5 were the reasons people moves to sealed traits
sealed trait MyEnum extends Product with Serializable
object MyEnum {
case object One extends MyEnum
case object Two extends MyEnum
case object Three extends MyEnum
}
they work better with pattern matching, derivation, it is easier to customize them (you just add things to common trait/common abstract class), etc.
In Scala 3 it got simplified to
enum MyEnum:
case One, Two, Three
All in all, there is virtually 0 reasons to use Enumeration other than legacy code.
I'd like to know what the proper syntax is for accessing an enum inside a class, from both inside and outside it.
My code:
final class Celig {
object eNums extends Enumeration {
type eNums = Value
val NAM, YEE = Value
}
// Here I'd like to assign a method that always returns a specific enum value
var nammy = eNums.NAM
def getNam: eNums.eNums = nammy
}
This code compiles, but it's on the ugly side.
I would like to know if a value of the Enum can be accessed in a cleaner way from inside the class, for example without the "eNums.eNums", and I'm also curious as to how to access a value of the enum from outside the class.
Edit: I would also like to know if it is possible to use a value in the default constructor that takes a value of the eNums type, for example, adding (car: Celig.eNums ) to the class header.
Thanks
Does something like the following do what you're looking for?
final class Celig {
object ENums extends Enumeration {
val NAM, YEE = Value
}
def getNam: ENums.Value = ENums.NAM
}
I would caution that Enumeration is generally not used very much in the world of Scala. Often we use sealed traits or sealed abstract classes instead to simulate algebraic datatypes which subsume Enumerations.
In particular I would've probably rewritten your code something like the following:
sealed trait ENums
case object NAM extends ENums
case object YEE extends ENums
final class Celig {
def getNam: ENums = NAM
}
This way of doing things has nice compiler support in the form of exhaustiveness checking for match statements and also allows for richer hierarchies than what Enumeration offers.
sealed trait ENums
case object NAM extends ENums
case object YEE extends ENums
final class Celig {
def switchOnEnums(x: ENums): Int = x match {
case NAM => 1
}
}
results in
[info] Set current project to Test 1 (in build file:/scalastuff/)
[info] Compiling 1 Scala source to /scalastuff/target/scala-2.11/classes...
[warn] /scalastuff/src/main/scala/Main.scala:35: match may not be exhaustive.
[warn] It would fail on the following input: YEE
[warn] def switchOnEnums(x: ENums): Int = x match {
[warn] ^
[warn] one warning found
[success] Total time: 5 s, completed Sep 16, 2016 1:13:35 AM
And for a more complex hierarchy that still enjoys the compiler assisted exhaustiveness checking from before
sealed trait Day
case object Monday extends Day
case object Tuesday extends Day
// Got tired of writing days...
sealed trait Month
case object January extends Month
case object February extends Month
// Got tired of writing months...
sealed trait Date
case class CalendarFormat(day: Day, month: Month, year: Int) extends Date
case class UnixTime(sinceUTC: BigInt) extends Date
in java i've got enum say
enum Num {
ONE,
TWO,
THREE;
private static Num current = ONE;
private static Num next = TWO;
public Num getNext(){
return values()[(ordinal() + 1) % values().length];
}
public static Num getNextNum(){
next = current.getNext()
}
}
so i'm able to assign next in that fashion calling getNextNum(), but scala enums, as it seems to me, lacks this feature. do u know any other workaround except using java enum in scala code?
Take a look at Case objects vs Enumerations in Scala
In Scala, Enumeration has limited functionality and it's preferable to have sealed traits if you want to have functionality added to your Enumeration.
For example:
sealed trait Num {
val next: Num
}
case object ONE extends Num {
override val next = TWO
}
case object TWO extends Num {
override val next = THREE
}
case object THREE extends Num {
override lazy val next = ONE
}
Also in your case, it's a bit tricky since you want to refer to a value before it's initialized. So there is a lazy on the last definition (you can also define next as def, if you want to)
In case you wanted to achieve this behavior using standard Scala Enumeration, you can redefine Enumeration#Value as instances of your enum:
object Num extends Enumeration {
case class MyValue() extends Val {
def next: MyValue = Num((id + 1) % Num.values.size).asInstanceOf[MyValue]
}
val One, Two, Three = MyValue()
}
Num.One.next
// res0: Num.MyValue = Two
However, since Enumerations are bad due to so many reasons, as well as don't have consistent IDE support, I personally would use sealed traits with case objects for enums (check out enumeratum) which allow to introduce arbitrary logic for my enums due to precise control over what's going on.
I am a newbie to scala.
So far i learned that, object in scala is singleton and if we declare case object, then override and hashcode default implementations are also added.
Just wondering to find any simple practical example, where we can fit case object.
Edit 1:
#Aivean :-
But without declaring object as case, below code also works fine :-
object ScalaPractice {
def main(args: Array[String]): Unit = {
val trade1 = Trade(EUR)
trade1.currency match{
case EUR | USD => println("trade possiblein this currency : " + trade1.currency)
case _ => println("trade not possible")
}
}
}
case class Trade(val currency : Currency){
}
sealed trait Currency { def name: String }
object EUR extends Currency { val name = "EUR" }
object USD extends Currency { val name = "USD" }
Why it is required to add case ?
Edit 2:
#dwickern
As #dwickern quoted :-
Converting your object to a case object gives you:
A slightly nicer default toString implementation
The case object automatically implements Serializable. This can be important, for example when passing these objects as messages via akka remoting
You also get default equals, hashCode and scala.Product implementations just like a case class but these really don't really matter
Is there any official documentation for this on scala website, scal docs, etc.. (specially for the third point, that is in bold italics)
Converting your object to a case object gives you:
A slightly nicer default toString implementation
The case object automatically implements Serializable. This can be important, for example when passing these objects as messages via akka remoting
For example:
case object USD
object EUR
println(USD) // USD
println(EUR) // testapp$EUR$#edf4efb
val oos = new ObjectOutputStream(new ByteArrayOutputStream())
oos.writeObject(USD)
oos.writeObject(EUR) // java.io.NotSerializableException
You also get default equals, hashCode and scala.Product implementations just like a case class but these really don't really matter
One of good examples is with actor model. Here case object can represent a message to send to an actor:
case object Start
someActorRef ! Start
Here this object doesn't contain any data it just serves the protocol of messaging - indicates that actor should execute some start logic.
Another example if you writing API for remote control, your API can take as arguments commands like: turn left, turn right, etc. It can be good idea to implement these commands as case objects - they doesn't have any state:
case object Left
case object Right
case object Up
case object Down
Case objects are handy when used as enums (example from here):
sealed trait Currency { def name: String }
case object EUR extends Currency { val name = "EUR" }
case object USD extends Currency { val name = "USD" }
Case objects can be used along with case classes in order to provide immutable implementation for some corner cases.
Check the Nil from scala.collection.immutable:
case object Nil extends List[Nothing]
For a project of mine I have implemented a Enum based upon
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
from Case objects vs Enumerations in Scala. I worked quite nice, till I run into the following problem. Case objects seem to be lazy and if I use Currency.value I might actually get an empty List. It would have been possible to make a call against all Enum Values on startup so that the value list would be populated, but that would be kind of defeating the point.
So I ventured into the dark and unknown places of scala reflection and came up with this solution, based upon the following SO answers. Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?
and How can I get the actual object referred to by Scala 2.10 reflection?
import scala.reflect.runtime.universe._
abstract class Enum[A: TypeTag] {
trait Value
private def sealedDescendants: Option[Set[Symbol]] = {
val symbol = typeOf[A].typeSymbol
val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
if (internal.isSealed)
Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol)
else None
}
def values = (sealedDescendants getOrElse Set.empty).map(
symbol => symbol.owner.typeSignature.member(symbol.name.toTermName)).map(
module => reflect.runtime.currentMirror.reflectModule(module.asModule).instance).map(
obj => obj.asInstanceOf[A]
)
}
The amazing part of this is that it actually works, but it is ugly as hell and I would be interested if it would be possible to make this simpler and more elegant and to get rid of the asInstanceOf calls.
Here is a simple macro based implementation:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
abstract class Enum[E] {
def values: Seq[E] = macro Enum.caseObjectsSeqImpl[E]
}
object Enum {
def caseObjectsSeqImpl[A: c.WeakTypeTag](c: blackbox.Context) = {
import c.universe._
val typeSymbol = weakTypeOf[A].typeSymbol.asClass
require(typeSymbol.isSealed)
val subclasses = typeSymbol.knownDirectSubclasses
.filter(_.asClass.isCaseClass)
.map(s => Ident(s.companion))
.toList
val seqTSymbol = weakTypeOf[Seq[A]].typeSymbol.companion
c.Expr(Apply(Ident(seqTSymbol), subclasses))
}
}
With this you could then write:
sealed trait Currency
object Currency extends Enum[Currency] {
case object USD extends Currency
case object EUR extends Currency
}
so then
Currency.values == Seq(Currency.USD, Currency.EUR)
Since it's a macro, the Seq(Currency.USD, Currency.EUR) is generated at compile time, rather than runtime. Note, though, that since it's a macro, the definition of the class Enum must be in a separate project from where it is used (i.e. the concrete subclasses of Enum like Currency). This is a relatively simple implementation; you could do more complicated things like traverse multilevel class hierarchies to find more case objects at the cost of greater complexity, but hopefully this will get you started.
A late answer, but anyways...
As wallnuss said, knownDirectSubclasses is unreliable as of writing and has been for quite some time.
I created a small lib called Enumeratum (https://github.com/lloydmeta/enumeratum) that allows you to use case objects as enums in a similar way, but doesn't use knownDirectSubclasses and instead looks at the body that encloses the method call to find subclasses. It has proved to be reliable thus far.
The article "“You don’t need a macro” Except when you do" by Max Afonov
maxaf describes a nice way to use macro for defining enums.
The end-result of that implementation is visible in github.com/maxaf/numerato
Simply create a plain class, annotate it with #enum, and use the familiar val ... = Value declaration to define a few enum values.
The #enum annotation invokes a macro, which will:
Replace your Status class with a sealed Status class suitable for acting as a base type for enum values. Specifically, it'll grow a (val index: Int, val name: String) constructor. These parameters will be supplied by the macro, so you don't have to worry about it.
Generate a Status companion object, which will contain most of the pieces that now make Status an enumeration. This includes a values: List[Status], plus lookup methods.
Give the above Status enum, here's what the generated code looks like:
scala> #enum(debug = true) class Status {
| val Enabled, Disabled = Value
| }
{
sealed abstract class Status(val index: Int, val name: String)(implicit sealant: Status.Sealant);
object Status {
#scala.annotation.implicitNotFound(msg = "Enum types annotated with ".+("#enum can not be extended directly. To add another value to the enum, ").+("please adjust your `def ... = Value` declaration.")) sealed abstract protected class Sealant;
implicit protected object Sealant extends Sealant;
case object Enabled extends Status(0, "Enabled") with scala.Product with scala.Serializable;
case object Disabled extends Status(1, "Disabled") with scala.Product with scala.Serializable;
val values: List[Status] = List(Enabled, Disabled);
val fromIndex: _root_.scala.Function1[Int, Status] = Map(Enabled.index.->(Enabled), Disabled.index.->(Disabled));
val fromName: _root_.scala.Function1[String, Status] = Map(Enabled.name.->(Enabled), Disabled.name.->(Disabled));
def switch[A](pf: PartialFunction[Status, A]): _root_.scala.Function1[Status, A] = macro numerato.SwitchMacros.switch_impl[Status, A]
};
()
}
defined class Status
defined object Status