4

I have read that case classes can be used with pattern matching. But, I am able to use a regular class with pattern matching as well. This question gives a regular scala perspective. I want it both from scala as well as akka perspective for this specific code. For example:

Actor class:

class TestActor extends Actor {

  def receive={
    case One(num)=>println("One "+num)
    case t:Two=>println("Two "+t.num)
    case _=>println("Another")
  }
}

object TestActor{
  case class One(num:Int)
}

Class Two:

class Two(var num:Int){  }

Main:

object Main2 extends App{

  val system=ActorSystem("t")
  val testActor=system.actorOf(Props[TestActor],"test")

  val t=new Two(200)
  val o=TestActor.One(100)

  testActor!o
  testActor!t

}

Output is:

One 100
Two 200

I know I am missing something here, probably in my understanding of pattern matching. Can someone help me out?

Community
  • 1
  • 1
codingsplash
  • 4,785
  • 12
  • 51
  • 90
  • Note that you can't write `case Two(t)` because case classes come with a default `unapply` method that enables that, and regular classes don't (though you can write one). – Tzach Zohar Jan 02 '17 at 07:03
  • If my code works with case t:Two, why would I need case class? – codingsplash Jan 02 '17 at 07:15
  • 1
    Behavior will be the same, of course - in this case it's mostly a matter of readability - `Two(num)` allows you to name the inner variable however you want - to make your _intent_ in _this specific context_ clearer, e.g. `case Two(receivedNum)` or something. As the answer you referenced states, there are other benefits to case classes (e.g. default `equals` and `hashCode`), but none of them would have been used in this specific code. Still - it's good practice to use case classes for classes that are "data containers" - if and when they will be used differently, those might come in handy. – Tzach Zohar Jan 02 '17 at 07:22

1 Answers1

4

As you've noticed, the main difference with case classes in Akka is the ability to use their extractors (unapply methods) when matching (other than the usual features you get our of case classes, such as an implementation for hashCode, equals, and working with them as a product type).

With a larger example, you can see how this plays out nice. For example, assume you're using Akka-HTTP client to make HTTP requests to a third party service, and you want to operate differently if you get a StatusCode.OK, or any other status code. Because HttpResponse provides an unapply method, you can do:

case HttpResponse(StatusCodes.OK, _, entity, _)) => // We're good, parse the data.
case HttpResponse(statusCode, _, _, _)) => // Operate on status code != OK

Instead of:

case response: HttpResponse =>
  if (response.statusCode != StatusCodes.OK) {
    // Stuff
  }
  else {
    // Other stuff
  }

When your logic becomes more complex, extracting only the values you want can be very helpful and less verbose. In our codebase we have something more complex with multiple values, something like:

case (Foo(bar, baz), resp@HttpResponse(statusCode, _, _, _))

Of course, you can always create an unapply method in a companion object yourself and get the same semantics of a case class yourself, but you usually don't want to be writing that boilerplate, allowing the compiler to do it for you.

To sum up, using case class as your data container in Akka eases the work with pattern matching, which is often what you do when implementing a receive method.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321