* Add how to compose Partial Behaviors #29832 Refs: #29832 Adds 2 examples on how to compose partial behaviors: - Using `receivePartial` - Using `PartialFunction` Examples are rewritten to follow the main one in the page. Instead of composing through Functions of `Command => Behavior[Command]` I decided to use `Behaviors.receivePartial` as it seems more idiomatic. Adds docs for both examples under the style guide page. * Remove potentially dangerous `receivePartial` composition Example now only showcases `PartialFunction` composition * Remove unused import * Streamline example
This commit is contained in:
parent
2c659a046e
commit
388fb73beb
2 changed files with 78 additions and 0 deletions
|
|
@ -24,6 +24,7 @@ import akka.actor.typed.scaladsl.ActorContext
|
|||
import akka.actor.typed.scaladsl.Behaviors
|
||||
//#fun-style
|
||||
import akka.actor.typed.scaladsl.AbstractBehavior
|
||||
import org.slf4j.Logger
|
||||
//#oo-style
|
||||
|
||||
object StyleGuideDocExamples {
|
||||
|
|
@ -522,6 +523,54 @@ object StyleGuideDocExamples {
|
|||
}
|
||||
}
|
||||
|
||||
object BehaviorCompositionWithPartialFunction {
|
||||
|
||||
//#messages-sealed-composition
|
||||
sealed trait Command
|
||||
case object Down extends Command
|
||||
final case class GetValue(replyTo: ActorRef[Value]) extends Command
|
||||
final case class Value(n: Int)
|
||||
//#messages-sealed-composition
|
||||
|
||||
//#get-handler-partial
|
||||
def getHandler(value: Int): PartialFunction[Command, Behavior[Command]] = {
|
||||
case GetValue(replyTo) =>
|
||||
replyTo ! Value(value)
|
||||
Behaviors.same
|
||||
}
|
||||
//#get-handler-partial
|
||||
|
||||
//#set-handler-non-zero-partial
|
||||
def setHandlerNotZero(value: Int): PartialFunction[Command, Behavior[Command]] = {
|
||||
case Down =>
|
||||
if (value == 1)
|
||||
zero
|
||||
else
|
||||
nonZero(value - 1)
|
||||
}
|
||||
//#set-handler-non-zero-partial
|
||||
|
||||
//#set-handler-zero-partial
|
||||
def setHandlerZero(log: Logger): PartialFunction[Command, Behavior[Command]] = {
|
||||
case Down =>
|
||||
log.error("Counter is already at zero!")
|
||||
Behaviors.same
|
||||
}
|
||||
//#set-handler-zero-partial
|
||||
|
||||
//#top-level-behaviors-partial
|
||||
val zero: Behavior[Command] = Behaviors.setup { context =>
|
||||
Behaviors.receiveMessagePartial(getHandler(0).orElse(setHandlerZero(context.log)))
|
||||
}
|
||||
|
||||
def nonZero(capacity: Int): Behavior[Command] =
|
||||
Behaviors.receiveMessagePartial(getHandler(capacity).orElse(setHandlerNotZero(capacity)))
|
||||
|
||||
// Default Initial Behavior for this actor
|
||||
def apply(initialCapacity: Int): Behavior[Command] = nonZero(initialCapacity)
|
||||
//#top-level-behaviors-partial
|
||||
}
|
||||
|
||||
object NestingSample1 {
|
||||
sealed trait Command
|
||||
|
||||
|
|
|
|||
|
|
@ -412,6 +412,35 @@ Scala
|
|||
|
||||
@@@ div {.group-scala}
|
||||
|
||||
## How to compose Partial Functions
|
||||
|
||||
Following up from previous section, there are times when one might want to combine different `PartialFunction`s into one `Behavior`.
|
||||
|
||||
A good use case for composing two or more `PartialFunction`s is when there is a bit of behavior that repeats across different states of the Actor. Below, you can find a simplified example for this use case.
|
||||
|
||||
The Command definition is still highly recommended be kept within a `sealed` Trait:
|
||||
|
||||
Scala
|
||||
: @@snip [StyleGuideDocExamples.scala](/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala) { #messages-sealed-composition }
|
||||
|
||||
In this particular case, the Behavior that is repeating over is the one in charge to handle
|
||||
the `GetValue` Command, as it behaves the same regardless of the Actor's internal state.
|
||||
Instead of defining the specific handlers as a `Behavior`, we can define them as a `PartialFunction`:
|
||||
|
||||
Scala
|
||||
: @@snip [StyleGuideDocExamples.scala](/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala) { #get-handler-partial #set-handler-non-zero-partial #set-handler-zero-partial }
|
||||
|
||||
Finally, we can go on defining the two different behaviors for this specific actor. For each `Behavior` we would go and concatenate all needed `PartialFunction` instances with `orElse` to finally apply the command to the resulting one:
|
||||
|
||||
Scala
|
||||
: @@snip [StyleGuideDocExamples.scala](/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala) { #top-level-behaviors-partial }
|
||||
|
||||
Even though in this particular example we could use `receiveMessage` as we cover all cases, we use `receiveMessagePartial` instead to cover potential future unhandled message cases.
|
||||
|
||||
@@@
|
||||
|
||||
@@@ div {.group-scala}
|
||||
|
||||
## ask versus ?
|
||||
|
||||
When using the `AskPattern` it's recommended to use the `ask` method rather than the infix `?` operator, like so:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue