Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pattern match large Scala case classes?

Consider the following Scala case class:

case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)

Pattern matching allows me to extract one field and discard others, like so:

someVal match {
    case WideLoad(_, _, _, d, _) => d ! SomeMessage(...)
}

What I would like to do, and what's more relevant when a case class has ~20 odd fields, is to extract only a few values in a way that does not involve typing out WideLoad(_, _, _, _, _, some, _, _, _, thing, _, _, interesting).

I was hoping that named args could help here, although the following syntax doesn't work:

someVal match {
    case WideLoad(d = dActor) => dActor ! SomeMessage(...)
    //              ^---------- does not compile
}

Is there any hope here, or am I stuck typing out many, many _, _, _, _?

EDIT: I understand that I can do case wl @ WideLoad(...whatever...) => wl.d, yet I'm still wondering whether there's even terser syntax that does what I need without having to introduce an extra val.

like image 838
Max A. Avatar asked Aug 13 '10 05:08

Max A.


People also ask

How do you match a pattern in Scala?

A pattern match includes a sequence of alternatives, each starting with the keyword case. Each alternative includes a pattern and one or more expressions, which will be evaluated if the pattern matches. An arrow symbol => separates the pattern from the expressions.

How is case class used in pattern matching?

Case classes help us use the power of inheritance to perform pattern matching. The case classes extend a common abstract class. The match expression then evaluates a reference of the abstract class against each pattern expressed by each case class.

Does Scala have pattern matching?

Notes. Scala's pattern matching statement is most useful for matching on algebraic types expressed via case classes. Scala also allows the definition of patterns independently of case classes, using unapply methods in extractor objects.

What is case class and pattern matching in Scala?

It is defined in Scala's root class Any and therefore is available for all objects. The match method takes a number of cases as an argument. Each alternative takes a pattern and one or more expressions that will be performed if the pattern matches. A symbol => is used to separate the pattern from the expressions.


3 Answers

I don't know if this is appropriate, but you can also build an object just to match that field, or that set of fields (untested code):

object WideLoadActorRef {
  def unapply(wl: WideLoad): Option[ActorRef] = { Some(wl.d) }
}

someVal match {
  case WideLoadActorRef(d) => d ! someMessage
}

or even

object WideLoadBnD {
  def unapplySeq(wl: WideLoad): Option[(Int,ActorRef)] = { Some((wl.b,wl.d)) }
}

someVal match {
  case WideLoadBnD(b, d) => d ! SomeMessage(b)
}
like image 193
Magnus Avatar answered Sep 29 '22 16:09

Magnus


You can always fall back to guards. It's not really nice, but better than normal pattern matching for you monster case classes :-P

case class Foo(a:Int, b:Int, c:String, d:java.util.Date)

def f(foo:Foo) = foo match {
  case fo:Foo if fo.c == "X" => println("found")
  case _ => println("arrgh!")
}

f(Foo(1,2,"C",new java.util.Date())) //--> arrgh!
f(Foo(1,2,"X",new java.util.Date())) //--> found

That said I think you should rethink your design. Probably you can logically group some parameters together using case classes, tuples, lists, sets or maps. Scala does support nested pattern matching:

case class Bar(a: Int, b:String)
case class Baz(c:java.util.Date, d:String)
case class Foo(bar:Bar, baz:Baz)

def f(foo:Foo) = foo match {
   case Foo(Bar(1,_),Baz(_,"X")) => println("found")
   case _ => println("arrgh!")
}

f(Foo(Bar(1,"c"),Baz(new java.util.Date, "X"))) //--> found
f(Foo(Bar(1,"c"),Baz(new java.util.Date, "Y"))) //--> arrgh! 
like image 21
Landei Avatar answered Sep 29 '22 16:09

Landei


You can just specify the type in the matched pattern:

case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)

val someVal = WideLoad(...)

someVal match {
    case w: WideLoad => w.d ! SomeMessage(...)
}
like image 32
vaer-k Avatar answered Sep 29 '22 18:09

vaer-k