Reputation: 7504
I have following implementation for receive method in Akka actor implementation.
override def receive: Receive = {
case SetRequest(key, value) =>{
log.info("Received SetRequest - key:{} ,value:{}", key,value)
map.put(key,value)
sender() ! Status.Success
}
case GetRequest(key) => {
log.info("Received GetRequest for - key:{}", key)
val response: Option[Object] = map.get(key)
response match {
case Some(x) => sender() ! x
case None => sender() ! Status.Failure(new KeyNotFoundException(key))
}
}
case o => Status.Failure(new ClassNotFoundException())
}
I have few queries here.
In Actor.scala, receive is defined as:
def receive: Actor.Receive
Actor.Receive is:
type Receive = scala.PartialFunction[scala.Any, scala.Unit]
So how does my code in receive conforms to Actor.Receive?
Second, what is this style of pattern matching?receive seems to receive no argument, so what am I actually trying to match?For eg., in code I match response,which makes sense as response is calculated before it is pattern matched.
Upvotes: 0
Views: 1461
Reputation: 22449
So how does my code in receive conforms to Actor.Receive? ... What is this style of pattern matching?
In Scala, case
is a common kind of partial functions. For example:
val oneOrTwo: PartialFunction[Int, String] = {
case 1 => "one"
case 2 => "two"
}
// oneOrTwo: PartialFunction[Int,String] = <function1>
val reciprocal: PartialFunction[Double, Double] = { case i if i != 0 => 1 / i }
// reciprocal: PartialFunction[Double,Double] = <function1>
Hence case
partial functions can be used to implement receive
which has the PartialFunction[Any, Unit]
signature.
The Any
argument type allows you to use case
to check against an input
of any kind you wish (e.g. SetRequest(key, value)
, GetRequest(key)
in your sample code).
On the return type, Unit
allows you to put in any message processing code (e.g. sender() ! Status.Success
, response match {...}
in your sample code) and does not demand a return value.
receive
seems to receive no argument, so what am I actually trying to match?
Your class extends Actor
, so when overriding receive
, you need to implement the method declared in trait Actor
(see below, from Akka source code) which you already did with your case
partial function.
object Actor {
type Receive = PartialFunction[Any, Unit]
// ...
}
trait Actor {
def receive: Actor.Receive
// ...
}
By extending Actor
and implementing method receive
, Akka equips your class with all the message-driven actor features including a mailbox and designates a dispatcher to feed any messages sent to it (e.g. myActor ! GetRequest(key)
from another actor) as input
to the receive
method.
Upvotes: 2
Reputation: 317
A partial function comes from mathematics and is opposed to a total function. A total function is defined for all inputs like this one:
val f = (i: Int) => i + 10
Function f produces a valid result for any input of type Int. However this partial function:
val g = (i: Int) => 10/i
does not (zero would result in an error).
In Scala you can create these special functions like so:
val h: PartialFunction[Int, Int] = {
case i: Int if i != 0 => 10/i
}
It has a methdod isDefinedAt
which can be used to check if it produce a valid result for a given input:
h.isDefinedAt(0) // false
h.isDefinedAt(5) // true
You can call partial functions like normal functions:
h(5)
But they also allow you to chain and combine them like regular functions with orElse
. Example:
val x: PartialFunction[Int, String] = {
case i: Int if i = 10 => "ten"
}
val y: PartialFunction[Int, String] = {
case i: Int if i = 5 => five"
}
val z = y orElse x
z(10) // "ten"
z(5) // "five"
z(0) // MatchError
Alone they are able to handle one case each, combined they can handle two.
To your second question: receive
is an unimplemented method in the Actor trait. It will be called somewhere in the Akka implementation with your message. It returns a partial function so it can be combined with the default Akka messages like PoisonPill
and so on.
Upvotes: 0