Reputation: 311
I have some Akka-based actor system where multiple kind of actors is based on same template due to a fact that those actors only differ by a type of response value. For example:
final case class Request(data: Any)
final case class Response(data: Any)
abstract class ActorTemplate[T] extends Actor {
def dataFunction: PartialFunction[Any, T]
def respond(data: T): Response
def receive: Receive = {
case Request(data) if dataFunction.isDefinedAt(data) =>
sender ! respond(dataFunction(data))
}
}
Partial function there is a method to avoid type erasure, which prevents me from simple
def receive: Receive = {
case Request(data: T) =>
sender ! respond(data)
and at the same time force me to produce if dataFunction.isDefinedAt(data)
guard on pattern and I simply don't like it very much.
Is there any means to avoid explicit guarding?
Only way I found for now is to introduce some silly extractor:
object DataExtractor {
def unapply(data: Any): Option[T] =
if (dataFunction.isDefinedAt(data)) Some(dataFunction(data)) else None
}
def receive: Receive = {
case Request(DataExtractor(data) =>
sender ! respond(data)
But maybe it is done already somewhere in standard library? Or maybe there is some other way similar to collections collect
method, but for matching?
After some thoughts I've went back to extractor object and moved it into a trait with help of @pagoda_5b suggestions:
trait PFExtract[T] {
object PF {
def unapply(any: Any)(implicit pf: PartialFunction[Any, T]): Option[T] =
pf.lift(any)
}
}
Upvotes: 2
Views: 1029
Reputation: 7373
What I would actually do is use the applyOrElse
method on dataFunction
, defining a default response when the partial function is not defined for the given input
def defaultAnswer: T
def receive: Receive = {
case Request(data) =>
sender ! respond(dataFunction.applyOrElse(data, defaultAnswer))
}
If you prefer to wrap your answer in a Option
, there's already a method called lift
that does that for you, as you correctly guessed
def receive: Receive = {
case Request(data) =>
sender ! respond(dataFunction.lift(data))
}
You can even decide not to answer if the function is not defined for data
def receive: Receive = {
case Request(data) =>
dataFunction.lift(data).foreach {
sender ! respond(_)
}
}
foreach
defined on the Option
type runs the code within the closure only if there's some content available (i.e. if you have a Some(...)
)
Upvotes: 3