Partial Functions and Modularizing Actor Receive blocks


A partial function is defined as

trait PartialFunction[-A, +B] extends (A) ⇒ B

It is a unary function which defines a domain. Not all values of A would be a part of the domain. For instance, in the following code block

val sample = 1 to 10
val isEven: PartialFunction[Int, String] = {
  case x if x % 2 == 0 => x+" is even"
}

The odd values of A would not match the domain. We can check this by the abstract method isDefined which is available for partial functions.

abstract def isDefinedAt(x: A): Boolean

 isEven.isDefinedAt(12)                          //> res0: Boolean = true
 isEven.isDefinedAt(11)                           //> res1: Boolean = false

Hence the above defined partial function would match for even values and not for the odd values. Something like the following calls would result in the expected output

isEven(12)                      //> res2: String = 12 is even
isEven(11)                     //> scala.MatchError: 11 (of class java.lang.Integer)

Interestingly, PartialFunction offers a method called orElse which allows us to combine domains.

Hence, in the above scenario, if we have an alternate isOdd PartialFunction defined like this

  val isOdd: PartialFunction[Int, String] = {
    case x if x % 2 == 1 => x + " is odd"
  }

Then we can combine the 2 domains together with an orElse to get something like this

val numbers = (1 to 10) map (isEven orElse isOdd)
            //> numbers  : scala.collection.immutable.IndexedSeq[String] = Vector(1 is odd, 
            //| 2 is even, 3 is odd, 4 is even, 5 is odd, 6 is even, 7 is odd, 8 is even, 9 
            //| is odd, 10 is even)

Ok, so far so good. Now how do we take it to the actors. Simple, the Actor.Receive is a PartialFunction.

type Receive = PartialFunction[Any, Unit] 

So let us assume that we want to modularize code now on the basis of infrastructure events and our application events.

So say we define one receive block like this

def eventSourceReceive: Actor.Receive = {
    case RegisterListener(listener) => listeners = listeners :+ listener
    case UnregisterListener(listener) => listeners = listeners filter { _ != listener }
  }

and another one like this

def monitorReceive: Receive = {
    // Our rate of climb has changed
    case RateChange(amount) =>
      rateOfClimb = amount.min(1.0f).max(-1.0f) * maxRateOfClimb

    // Calculate a new heartRate
    case Tick =>
      val tick = System.currentTimeMillis
      heartRate = heartRate + ((tick - lastTick) / 60000.0) * rateOfClimb
      lastTick = tick
      sendEvent(MonitorUpdate(heartRate))
  }

and now we combine the 2 domains so that our actor is able to deal with both kinds of incoming events

def receive = eventSourceReceive orElse monitorReceive

This way, we would be able to keep our receive blocks modularized and still be able to handle all the messages by combining the domains together.

About Vikas Hazrati

Vikas is the CTO @ Knoldus which is a group of software industry veterans who have joined hands to add value to the art of software development. We do niche product and project development on Scala, Spark and Java. We consult and coach on effective software development and agile practices. With our focus on software craftsmanship you can be assured of a good quality at the right price. To know more, send a mail to info@knoldus.com or visit www.knoldus.com
This entry was posted in Architecture, Scala and tagged , , , . Bookmark the permalink.

One Response to Partial Functions and Modularizing Actor Receive blocks

  1. geluja says:

    Scala 2.11 gives an error on that last line of code in your (very nice) article: a type was inferred to be `Any`; this may indicate a programming error

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s