9

I already looked at the Scala question about emulating Java's enum and case classes vs. Enumeration but It seems too much effort for too less benefit.

Basically I would like to have a values method returning all singleton objects of DayOfWeek without repeating myself a few times.

This is how my code should look like:

object DayOfWeek extends MyEnum {
  object MONDAY extends DayOfWeek(1)
  object TUESDAY extends DayOfWeek(2)
  object WEDNESDAY extends DayOfWeek(3)
  object THURSDAY extends DayOfWeek(4)
  object FRIDAY extends DayOfWeek(5)
  object SATURDAY extends DayOfWeek(6)
  object SUNDAY extends DayOfWeek(7)
}

class DayOfWeek(ordinal: Int)

The method values should return something like if it had been written like this:

val values = Array(MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
                   FRIDAY, SATURDAY, SUNDAY)

Everything should happen in the MyEnum trait so I only need to extend it to get the functionality.

trait MyEnum {
  val values = this.getClass.getField("MODULE$") etc. etc.
}

Any suggestion how this could be done exactly? The idea is that values accesses the class and finds all singleton objects of the class they are extending.

Edit: It looks like all suggestions don't take in account that the user can create objects too which should of course be comparable to the defined ones.

I'll try to give another example, maybe it is more clear:

object MonthDay extends MyEnum {
  //Some important holidays
  object NewYear       extends MonthDay( 1,  1)
  object UnityDay      extends MonthDay(11,  9)
  object SaintNicholas extends MonthDay(12,  6)
  object Christmas     extends MonthDay(12, 24)
}

class MonthDay(month: Int, day: Int)

//Of course the user can create other MonthDays
val myBirthDay = new MonthDay(month, day)

if(!MonthDay.values.contains(myBirthDay)) "Well, I probably have to work"
else "Great, it is a holiday!"

I want to have a trait (MyEnum) which I can mix into the object holding my "enumeration objects" with methods to return a list of them (def values: List[MonthDay]) or iterate over them (def next: MonthDay or def previous: MonthDay).

PPS: I created a new question for the second part of this question as requested by Ken Bloom.

Community
  • 1
  • 1
soc
  • 27,983
  • 20
  • 111
  • 215
  • could you re-ask your amended question as a new question (phrased as a reflection question)? It really has nothing to do with Enumerations. – Ken Bloom Nov 21 '10 at 15:37
  • the `add` function that I proposed is still a workable solution to your amended question. – Ken Bloom Nov 21 '10 at 15:39
  • @Ken Bloom: Done, here it is: http://stackoverflow.com/questions/4239594/how-to-access-objects-within-an-object-by-mixing-in-a-trait-with-reflection – soc Nov 21 '10 at 18:54

3 Answers3

5

How about this? It requires you to actually call an add method for each new value, but values returns the right type.

abstract class MyEnum{

   type Value     //define me to be the value type for this MyEnum

   private var _values:List[Value] = Nil
   def values = _values    
   protected def add(newValue:Value) = {
      _values = newValue::_values
      newValue
   }
}

object DayOfWeek extends MyEnum{
   class Value(val dayNum:Int)
   val SUNDAY    = add(new Value(1))
   val MONDAY    = add(new Value(2))
   val TUESDAY   = add(new Value(3))
   val WEDNESDAY = add(new Value(4))
   val THURSDAY  = add(new Value(5))
   val FRIDAY    = add(new Value(6))
   val SATURDAY  = add(new Value(7))
}

You can now call

println(DayOfWeek.values map (_.dayNum))

If you need singleton objects that have different definitions of the methods on different objects, you can create anonymous classes like so:

add(new Value{
      override def dayNum=8
    })
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • 2
    Just as a side point, you want to use a `var` of type `List[Value]` internally, rather than a `val` of type `Array[Value]`, because you want the list of values returned to the user to be totally immutable. – Ken Bloom Nov 21 '10 at 04:51
3

scala.Enumeration does exactly what you want already.

I think you may be confused by Scala 2.7 versus Scala 2.8. The old question you cite about emulating Java's enum was written in the days of Scala 2.7, and though I can't go test what functionality Scala 2.7's Enumerations posessed, Scala 2.8's Enumerations certainly possess everything you're looking for.

You can't define values with object SUNDAY extends Value(1) because objects are initialized lazily.

Community
  • 1
  • 1
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • 1
    No. I looked at `scala.Enumeration` and it doesn't work. It is not possible to add methods to the corresponding class. Additionally user-instantiated instances of that class are not of the same type as the `Value`s from the Enumeration. – soc Nov 21 '10 at 02:51
  • 2
    I stand corrected. I didn't realize the goal was to add user-defined methods to the `Value` class. – Ken Bloom Nov 21 '10 at 04:53
  • @soc I don't understand why you think you are unable to add methods to Val from enumeration. Please see pattern C in this answer I just posted to another thread (and consider that you could add any method you like to the case class ChessPieceVal which extends Val: http://stackoverflow.com/a/25923651/501113 – chaotic3quilibrium Sep 18 '14 at 23:14
2

The closest I've been able to come up with is:

abstract class MyEnum(val displayName:String){
   protected object Value{
      var _values:List[Value] = Nil
      def values = _values
   }
   protected class Value (val value:Int){
      Value._values = this::Value._values
      override def toString = "%s(%d)".format(displayName,value)
   }
   def values = Value.values
}

trait Methods{
   def dayName
}

object DayOfWeek extends MyEnum("DayOfWeek"){
   val SUNDAY = new Value(1) with Methods{
      override def dayName = "Sunday"
   }
   val MONDAY = new Value(2) with Methods{
      override def dayName = "Monday"
   }
   val TUESDAY = new Value(3) with Methods{
      override def dayName = "Tuesday"
   }
   val WEDNESDAY = new Value(4) with Methods{
      override def dayName = "Wednesday"
   }
   val THURSDAY = new Value(5) with Methods{
      override def dayName = "Thursday"
   }
   val FRIDAY = new Value(6) with Methods{
      override def dayName = "Friday"
   }
   val SATURDAY = new Value(7) with Methods{
      override def dayName = "Saturday"
   }
}

I haven't figured out how to change the type of the _values variable to capture the full type Value with Methods.

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168