21

Consider this class:

class DateTime(year: Int, month: Int, day: Int)(hour: Int, minute: Int, second: Int)

how would the unapply method look like, if I would like to match against something like:

dt match {
  case DateTime(2012, 12, 12)(12, _, _) => // December 12th 2012, 12 o'clock
  /* ... */
}

I tried this:

def unapply(dt: DateTime) = 
  Some((dt.year, dt.month, dt.day),(dt.hour, dt.minute, dt.second))

But that didn't really work.

soc
  • 27,983
  • 20
  • 111
  • 215

3 Answers3

25

Case classes match (and do their other nifty things) only on the first set of parameters:

scala> case class A(i: Int)(j: Int) { }
defined class A

scala> A(5)(4) match { case A(5) => "Hi" }
res14: java.lang.String = Hi

scala> A(5)(4) == A(5)(9)
res15: Boolean = true

If it's not a case class, you can define the unapply to be anything you want, so it's really up to the implementer of the class. By default, there is no unapply, so you can match only on the type.

If you want to use the nifty case class features including being able to match and do equality on everything, but have some sort of division, you could nest case classes:

case class Time(hour: Int, minute: Int, second: Int) { }
case class Date(year: Int, month: Int, day: Int) { }
case class DateTime(date: Date, time: Time) { }

scala> val dt = DateTime(Date(2011,5,27), Time(15,21,50))
scala> dt match { case DateTime(Date(2011,_,_),Time(h,m,50)) => println(h + ":" + m) }
15:21
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
10

Just to build on Rex's answer, not only can you only pattern match on the first parameter block, but this behaviour is very much by design.

The more interesting question is why case classes, as algebraic data types, even support multiple parameter lists...

There's no strong enough justification to add special behaviour for case classes, and multiple parameter lists turn out to be pretty useful. In production code this facility is often only used to supply implicit arguments, which you quite naturally wouldn't want to pattern match.

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
  • 5
    Multiple parameter lists are nice because you can have multiple var args lists. – Paul Draper May 17 '15 at 23:52
  • Maybe that's a bit contrived example, but if you could say `case class MyCase[A <: WithBar](a: A)(b: A#Bar)` and get all "nifty features" out of the box, then it would be possible to create new instances like `val x = MyCase(FooWithBar(...))(Bar(...))`, without type annotation for `A`. With a signature such as `MyCase[A <: WithBar](a: A, b: A#Bar)` you have to instantiate with `val x = MyCase[FooWithBar](FooWithBar(...), Bar(...))`, because type inference would not work for `A#Bar` when specified in the same parameter list as parameter of type `A`. – falconepl Aug 20 '15 at 07:45
  • Another reason to avoid the case class temptation. They do not play well with many advanced features (or concepts) of the language. – matanster Apr 20 '16 at 10:47
1

It probably didn't work because Scala has no comma operator, and you're returning Some((a,b),(x,y)) from the extractor. If you used Some(((a,b,c),(x,y,z))) instead (i.e. a Tuple2[Tuple3[A,B,C],Tuple3[X,Y,Z]] I think it would probably work.

Alex Cruise
  • 7,939
  • 1
  • 27
  • 40