4
votes

The receive method defines an actor's behavior in Akka actors. I am looking for an approach that can give me all the different messages (and their types) an actor can process preferably at runtime in Scala.

1
That is not possible. You might want to look at akka-typed if you need such guarantees.rethab
Akka typed cannot give the "types" of the messages that are able to be handled. Scala is not a dependently typed language, so types are not first-class. Akka typed can enforce that only certain messages are able to be handled by a certain "actor", though.erip
No, there is not. The receive method is passed a PartialFunction[Any, Unit]. In akka typed, you necessarily specify the type of messages that can be handled.erip
Note also that through the use of context.become, an actor can dynamically change its behavior (the receive method is just the default if the actor hasn't context.becomed).Levi Ramsey
The behavior of a running actor is stored as a private field (behaviorStack in the ActorCell). If you could get access to that field, you could take its headto get the current behavior, which is a PartialFunction[Any, Unit]. From that, you can pass messages to the isDefinedAt method. The most that this will do is let you have a list of messages that the actor could process at that moment in time, which isn't quite what you're looking for, but that's OK because barring a major JVM exploit, you're not going to get access to that private field.Levi Ramsey

1 Answers

0
votes

Direct Answer

Unfortunately the functionality that you are asking for is not available with akka. The receive method is defined as:

type Receive = PartialFunction[Any, Unit]

abstract def receive : Actor.Receive

There is no way for a PartialFunction to enumerate all types that it can process. Further, once an Actor has been instantiated into an ActorRef you don't have access to the underlying receive method.

One alternative is to define your receive outside of the Actor implementation and then use the isDefinedAt method of PartialFunction to test a particular value:

object MyActor {
  val myPartial : Receive = {
    //receive functionality
  }
}

class MyActor extends Actor {
  override def receive : Receive = MyActor.myPartial
}

//test if a value can be processed by MyActor

val testValue = 42

val testValueIsDefined = MyActor.myPartial.isDefinedAt(testValue)

Indirect Answer

If you organize your code correctly then the foundation of your question becomes unnecessary.

I've found a good practice is to strictly declare what types of inputs an Actor can receive:

sealed trait MyActorInputs

case class Foo(value : Int) extends MyActorInputs
case class Bar(value : String) extends MyActorInputs

object MyActor {
  val processInput : MyActorInput => Unit = ???
}

class MyActor extends Actor {
  override def receive : Receive = {
    case input : MyActorInput => 
      MyActor.processInput(input)
    case unknown => 
      System.error.println(s"MyActor received unknown input: $unknown")
  }
}

This technique does not provide compiler time checks or strict guarantees, but if you adopt it across all of your Actors then it tends to make life easier for bigger projects. It would also allow you to use reflection to dynamically get a list of available input types.