5

I'm new to Scala, so be nice please. You'll have to excuse me if I'm missing something obvious.

I'm trying to create an enum like structure to represent the days of the week in Scala. I want a method that accepts a string that can either be the numbers 1 through 7, the full name of the day, or a three letter abbreviation, with any capitalisation, and returns the correct day. Ideally I want to be able to retrieve the correct day simply by writing DayOfWeek(_), which as far as I understand things means this method needs to be apply. The individual values also have to be subclasses (or mix in) a trait called CalendarField, which at the moment defines no methods or members.

This is my current attempt:

object DayOfWeek extends Enumeration with CalendarField {

    val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = Value

    def apply(v:String) = {
        v.toUpperCase match {
            case Int(x) => x match {
                case 1 => Sunday
                case 2 => Monday
                case 3 => Tuesday
                case 4 => Wednesday
                case 5 => Thursday                  
                case 6 => Friday
                case 7 => Saturday
                case _ => throw new IllegalArgumentException("Invalid value for day of week: " + v)
            }
            case "SUN" | "SUNDAY" => Sunday
            case "MON" | "MONDAY" => Monday
            case "TUE" | "TUEDAY" => Tuesday
            case "WED" | "WEDNESDAY" => Wednesday
            case "THU" | "THURSDAY" => Thursday
            case "FRI" | "FRIDAY" => Friday
            case "SAT" | "SATURDAY" => Saturday
            case _ => throw new IllegalArgumentException("Invalid value for day of week: " + v)
        }
    }
}

object Int {
    def unapply(s : String) : Option[Int] = try {
        Some(s.toInt)
    } catch {
        case _ : java.lang.NumberFormatException => None
    }
}

This fails on a few points:

  1. The individual values aren't subclasses of CalendarField - they are simply Values.
  2. I've defined an apply method but DayOfWeek(_) doesn't work - my (vague) guess this is because it expects to call a constructor and DayOfWeek is an object, not a class? Or is it because Enumeration(s:String) is already used? Is there a way round this?

Any suggestions on how to get round these problems, or neater solutions, would be greatly appreciated. Thanks.

Russell
  • 12,261
  • 4
  • 52
  • 75
  • Is there a reason you want to use `Enumeration` instead of case objects? – Kim Stebel May 20 '11 at 19:12
  • No - will case objects give me what I'm after? – Russell May 20 '11 at 19:28
  • `DayOfWeek(_)` doesn't work in some contexts because `Enumeration` already defines an `apply` method that takes an `Int` parameter -- in such contexts, you'll need to supply the argument type (i.e. `DayOfWeek(_: String)`. – Aaron Novstrup May 20 '11 at 19:38
  • case objects are often used where you might use an enum in Java, but it depends on your specific needs. How do you plan on using them? – Aaron Novstrup May 20 '11 at 19:39
  • See [Case classes vs Enumerations in Scala](http://stackoverflow.com/questions/1898932/case-classes-vs-enumerations-in-scala) – Aaron Novstrup May 20 '11 at 19:41
  • Thanks - I'll have a read of that question now. However, is it possible for me to define an `apply` method for a case class? AFAIK traits can't have companion objects so where do I define it? Or is there an alternative? – Russell May 20 '11 at 19:55
  • As far as how I plan to use them - pretty simply. All I want is to parse the types of input I mentioned above and then to be left with an object which, when I call toString on it, returns the appropriate day. – Russell May 20 '11 at 20:05
  • @Russell I'm not sure where traits came into the discussion, but they *can* have companion objects. In this case though, I'd probably define the `apply` method on a `DayOfWeek` object. – Aaron Novstrup May 20 '11 at 20:35

3 Answers3

6

I'd probably implement something like this:

sealed trait DayOfWeek
case object Monday extends DayOfWeek
case object Tuesday extends DayOfWeek
// etc.

object DayOfWeek {
   def unapply(s: String): Option[DayOfWeek] = s.toUpperCase match {
      case Int(x) => x match {
         case 1 => Some(Sunday)
         // ...
         case _ => None
      }
      case "SUN" | "SUNDAY" => Some(Sunday)
      // ...
      case _ => None
   }

   def apply(s: String): DayOfWeek = s match {
      case DayOfWeek(d) => d
      case _ => throw new IllegalArgumentException(
                   "Invalid value for day of week: " + s)
   }

}

Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
0
object WeekEnumerator{

sealed trait Week
object WeekDay extends Week
object WeekEnd extends Week

val sat, sun = WeekEnd
val mon, tue, wed, thr, fri = WeekDay

def isWorkingDay(day:WeekEnumerator.Week):Boolean = day match{
case day @ WeekEnd => false
case day @ WeekDay => true
}
}

Usage: WeekEnumerator.isWorkingDay(WeekEnumerator.sun)

0

Using case objects would solve issue 1, since case objects can have a common base class. Since that base class does not inherit from Enumeration you will not have problems with inherited apply methods. Here is some sample code:

sealed abstract class DayOfWeek {
  def apply(s:String) = {
    //...
  }
}

case object Monday extends DayOfWeek
case object Tuesday extends DayOfWeek
//and so on...
Kim Stebel
  • 41,826
  • 12
  • 125
  • 142