Add section on using scala3 union types (#695)
* Add section on using scala3 union types * Code formatting * Integrate PR feedback - Point out that Union types are Scala 3 specific
This commit is contained in:
parent
2ce80b17f7
commit
0a09ccc71e
5 changed files with 59 additions and 7 deletions
|
|
@ -138,19 +138,19 @@ class InteractionPatterns3Spec extends ScalaTestWithActorTestKit with AnyWordSpe
|
|||
sealed trait Command
|
||||
final case class Translate(site: URI, replyTo: ActorRef[URI]) extends Command
|
||||
|
||||
private type CommandAndResponse = Command | Backend.Response
|
||||
private type CommandAndResponse = Command | Backend.Response // (1)
|
||||
|
||||
def apply(backend: ActorRef[Backend.Request]): Behavior[Command] =
|
||||
def apply(backend: ActorRef[Backend.Request]): Behavior[Command] = // (2)
|
||||
Behaviors.setup[CommandAndResponse] { context =>
|
||||
|
||||
def active(inProgress: Map[Int, ActorRef[URI]], count: Int): Behavior[CommandAndResponse] = {
|
||||
Behaviors.receiveMessage[CommandAndResponse] {
|
||||
case Translate(site, replyTo) =>
|
||||
val taskId = count + 1
|
||||
backend ! Backend.StartTranslationJob(taskId, site, context.self)
|
||||
backend ! Backend.StartTranslationJob(taskId, site, context.self) // (3)
|
||||
active(inProgress.updated(taskId, replyTo), taskId)
|
||||
|
||||
case Backend.JobStarted(taskId) =>
|
||||
case Backend.JobStarted(taskId) => // (4)
|
||||
context.log.info("Started {}", taskId)
|
||||
Behaviors.same
|
||||
case Backend.JobProgress(taskId, progress) =>
|
||||
|
|
@ -164,7 +164,7 @@ class InteractionPatterns3Spec extends ScalaTestWithActorTestKit with AnyWordSpe
|
|||
}
|
||||
|
||||
active(inProgress = Map.empty, count = 0)
|
||||
}.narrow
|
||||
}.narrow // (5)
|
||||
}
|
||||
// #adapted-response
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
# Handling responses in Scala 3
|
||||
|
||||
Handling responses from other actors in Scala 3 is straightforward and in contrast with
|
||||
Scala 2, it doesn't require the utilisation of message adapters and response wrappers.
|
||||
|
||||
A distinction exists between an actor's public protocol (`Command `) and its internal
|
||||
protocol (`CommandAndResponse`). The latter is the union of the public protocol and all
|
||||
the responses the actor should understand. This is union is implemented with Scala 3's
|
||||
Union types.
|
||||
|
||||
**Example:**
|
||||
|
||||

|
||||
|
||||
Scala
|
||||
: @@snip [InteractionPatternsSpec.scala](/actor-typed-tests/src/test/scala-3/docs/org/apache/pekko/typed/InteractionPatterns3Spec.scala) { #adapted-response }
|
||||
|
||||
Let's have a look at the key changes with respect to the Pekko typed implementation in
|
||||
Scala 2 (see the corresponding numbering in the example code).
|
||||
|
||||
* The type `CommandAndResponse` is the union of `Command` and `Backend.Response` (1)
|
||||
* In the factory method (2) for the `Behavior` of the frontend actor, a
|
||||
`Behavior[CommandAndResponse]` is narrowed (5) to a `Behavior[Command]`. This works as
|
||||
the former is able to handle a superset of the messages that can be handled by the latter.
|
||||
* The sending actor just sends its `self` @apidoc[actor.typed.ActorRef] in the `replyTo`
|
||||
field of the message (3)
|
||||
* Responses are handled in a straightforward manner (4)
|
||||
|
||||
A more in-depth explanation of the concepts used in applying Scala 3's Union types can
|
||||
be found in the following blog posts:
|
||||
|
||||
* [Using Dotty Union types with Akka Typed](https://blog.lunatech.com/posts/2020-02-12-using-dotty-union-types-with-akka-typed)
|
||||
* [Using Dotty Union types with Akka Typed - Part II](https://blog.lunatech.com/posts/2020-02-19-using-dotty-union-types-with-akka-typed-part-II)
|
||||
|
||||
**Useful when:**
|
||||
|
||||
* Subscribing to an actor that will send [many] response messages back
|
||||
|
||||
**Problems:**
|
||||
|
||||
* It is hard to detect that a message request was not delivered or processed
|
||||
* Unless the protocol already includes a way to provide context, for example a request id
|
||||
that is also sent in the response, it is not possible to tie an interaction to some
|
||||
specific context without introducing a new, separate, actor
|
||||
BIN
docs/src/main/paradox/typed/images/adapted-response-scala-3.png
Normal file
BIN
docs/src/main/paradox/typed/images/adapted-response-scala-3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
|
|
@ -10,6 +10,7 @@ project.description: Using Apache Pekko to build reliable multi-core application
|
|||
* [actors](actors.md)
|
||||
* [actor-lifecycle](actor-lifecycle.md)
|
||||
* [interaction patterns](interaction-patterns.md)
|
||||
* [handling responses with Scala 3](handling-actor-responses-with-scala3.md)
|
||||
* [fault-tolerance](fault-tolerance.md)
|
||||
* [actor-discovery](actor-discovery.md)
|
||||
* [routers](routers.md)
|
||||
|
|
|
|||
|
|
@ -103,7 +103,8 @@ Java
|
|||
|
||||
**Problems:**
|
||||
|
||||
* Actors seldom have a response message from another actor as a part of their protocol (see @ref:[adapted response](#adapted-response))
|
||||
* Actors seldom have a response message from another actor as a part of their protocol as it can be considered
|
||||
as polluting that protocol with a message from another actor's message (see @ref:[adapted response](#adapted-response))
|
||||
* It is hard to detect that a message request was not delivered or processed (see @ref:[ask](#request-response-with-ask-between-two-actors))
|
||||
* Unless the protocol already includes a way to provide context, for example a request id that is also sent in the
|
||||
response, it is not possible to tie an interaction to some specific context without introducing a new,
|
||||
|
|
@ -112,7 +113,13 @@ Java
|
|||
|
||||
## Adapted Response
|
||||
|
||||
Most often the sending actor does not, and should not, support receiving the response messages of another actor. In such cases we need to provide an @apidoc[actor.typed.ActorRef] of the right type and adapt the response message to a type that the sending actor can handle.
|
||||
Most often the sending actor does not, and should not, support receiving the response messages of another actor.
|
||||
|
||||
In such cases we need to provide an @apidoc[actor.typed.ActorRef] of the right type and adapt the response message
|
||||
to a type that the sending actor can handle. In the case of Scala, we need to make a distinction between Scala 2
|
||||
and Scala 3. In the latter case, we can actually get rid of the need to adapt the response message by leveraging
|
||||
Scala 3's Union types, which vastly simplifies the handling of responses. The details can be found in the
|
||||
section @ref:[Handling actor responses in Scala 3](handling-actor-responses-with-scala3.md).
|
||||
|
||||
**Example:**
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue