liveBook · Manning

Actor behavior

As explained in Akka in Action, Second Edition

An actor behavior defines how an actor processes its next message and what behavior it uses afterward.

An actor behavior defines how an actor handles one incoming message and selects what will handle the next one. This keeps processing sequential inside the actor: focus on one message at a time rather than coordinating shared mutable state. A behavior may remain unchanged, transition to another behavior, or stop the actor. section 1.6.2 section 2.6

Receive a message and keep the same behavior

A typed wallet can use Behavior[Int] with Behaviors.receive. The receive function gets an ActorContext and a message, logs the deposited amount, and returns Behaviors.same. Returning the same behavior means the next message is handled in the same way. section 2.2.4

Behaviors can also carry state through function parameters. A wallet behavior receiving Increase computes a new total and returns a fresh behavior such as apply(current, max). If the total exceeds its limit, it returns Behaviors.stopped instead. This represents state message by message without exposing it to other actors. section 2.3

Use distinct behaviors when state changes the meaning of the same message, not merely a stored value. An activated wallet accepts Increase and returns its activated behavior with an updated total. A deactivated wallet discards Increase, remains deactivated, and switches back only after Activate. Such behavior states form a finite state machine. section 2.4

Listing 2.8 WalletOnOff actor
object WalletOnOff {
 
  sealed trait Command
  final case class Increase(amount: Int) extends Command
  final case object Deactivate extends Command
  final case object Activate extends Command
 
  def apply(): Behavior[Command] = activated(0)           #1
 
  def activated(total: Int): Behavior[Command] =          #2
    Behaviors.receive { (context, message) =>
      message match {
        case Increase(amount) =>
          val current = total + amount
          context.log.info(s"increasing to $current")
          resume(current)
        case Deactivate =>
          deactivated(total)
        case Activate =>
          Behaviors.same
      }
    }
 
  def deactivated(total: Int): Behavior[Command] = {      #3
    Behaviors.receive { (context, message) =>
      message match {
        case Increase =>
          context.log.info(s"wallet is deactivated. Can't increase")
          Behaviors.same
        case Deactivate =>
          Behaviors.same
        case Activate =>
          context.log.info(s"activating")
          activated(total)
      }
    }
}

Compare the command protocol with the activated and deactivated handlers to see each message’s behavior transition. section 2.4

When defining the next behavior, choose the result that matches the actor’s state transition:

  • Return Behaviors.same when the current handler should continue unchanged. section 2.2.4
  • Return another behavior function when a message changes how later messages are interpreted. section 2.4
  • Return Behaviors.stopped when processing detects an unacceptable state. section 2.3

How Akka in Action, Second Edition covers it

How deep it goes
Works through receive handlers, Behaviors.same, recursive state-carrying behaviors, stopping, and two-state finite-state machines.
Examples
Uses wallet deposits, bounded totals that stop, and activated or deactivated wallets that react differently to Increase.
What it assumes
Introduces behavior through a minimal wallet while assuming basic Akka actor creation and message sending.
What you can do after
Build it from scratch with Behaviors.receive, Behaviors.same, behavior transitions, and Behaviors.stopped.
How it connects
Connects actor behavior to message protocols, ActorContext, actor state, finite state machines, and Behaviors.withTimers.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Akka in Action, Second Edition. The section links point to the parts of the book it draws on.