I had worked on Akka actors (non typed), recently started to write Akka typed actors, and found two ways of achieving one were the functional way and another was the object-oriented way (similar to old).
I was interested in understanding the encapsulation of state in both functional and object-oriented way. So wrote a method for the functional way and a class for the object-oriented way.
Functional Way:
def webSocketConnections(l: List[ActorRef[Message]] = List.empty): Behavior[WebSocketMsg] = {
Behaviors.receive[WebSocketMsg] {
(context, message) => {
message match {
case Model.UserAdded(actorRef) => webSocketConnections(actorRef :: l)
case Model.BroadcastToAll(msg) =>
context.spawnAnonymous(broadCastActorBehaviour) ! Broadcast(l, msg)
Behaviors.same
}
}
}
}
Object-Oriented Way
class WebSocketConnectionMaintainer(actorContext: ActorContext[WebSocketMsg]) extends
AbstractBehavior[WebSocketMsg](actorContext) {
private var actorRefL: List[ActorRef[Message]] = List.empty
override def onMessage(msg: WebSocketMsg): Behavior[WebSocketMsg] = {
msg match {
case Model.UserAdded(actorRef) =>
actorRefL = actorRef :: actorRefL
Behaviors.same
case Model.BroadcastToAll(msg) =>
actorContext.spawnAnonymous(broadCastActorBehaviour) ! Broadcast(actorRefL, msg)
Behaviors.same
}
}
}
If you observe both the cases, in the case of functional way the state gets encapsulated as a param, not sure whether it can have issues with respect to stack safety, as it can crash due to stack overflow error after a lot of calls right?
But if you observe the object-oriented way, it's just mutating the list directly which won't cause any stack overflow issue.
And another problem that I was finding is, I can only use context.spawn
for spawning a functional way actor, but in the case of object-oriented, I need a context of a particular type.
What to do if I want to create both functional and object-oriented actors together?
Regarding the stack safety concern, the short answer is that the apparent recursion is trampolined, so it won't blow the stack. See this StackOverflow question.
For spawning an OO-defined typed actor, you use Behaviors.setup
which injects the context:
def ooBehavior: Behavior[WebSocketMsg] = Behaviors.setup { ctx =>
new WebSocketConnectionMaintainer(ctx)
}
context.spawn(ooBehavior)