This is an automated email from the ASF dual-hosted git repository.
hepin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-pekko.git
The following commit(s) were added to refs/heads/main by this push:
new 0a09ccc71e Add section on using scala3 union types (#695)
0a09ccc71e is described below
commit 0a09ccc71e28efac5250d448d2e65698b83f508c
Author: Eric Loots <[email protected]>
AuthorDate: Wed Oct 11 13:19:27 2023 +0200
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
---
.../pekko/typed/InteractionPatterns3Spec.scala | 10 ++---
.../typed/handling-actor-responses-with-scala3.md | 44 +++++++++++++++++++++
.../typed/images/adapted-response-scala-3.png | Bin 0 -> 53066 bytes
docs/src/main/paradox/typed/index.md | 1 +
.../src/main/paradox/typed/interaction-patterns.md | 11 +++++-
5 files changed, 59 insertions(+), 7 deletions(-)
diff --git
a/actor-typed-tests/src/test/scala-3/docs/org/apache/pekko/typed/InteractionPatterns3Spec.scala
b/actor-typed-tests/src/test/scala-3/docs/org/apache/pekko/typed/InteractionPatterns3Spec.scala
index 26257fe287..404b955208 100644
---
a/actor-typed-tests/src/test/scala-3/docs/org/apache/pekko/typed/InteractionPatterns3Spec.scala
+++
b/actor-typed-tests/src/test/scala-3/docs/org/apache/pekko/typed/InteractionPatterns3Spec.scala
@@ -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
diff --git
a/docs/src/main/paradox/typed/handling-actor-responses-with-scala3.md
b/docs/src/main/paradox/typed/handling-actor-responses-with-scala3.md
new file mode 100644
index 0000000000..5f5cee9092
--- /dev/null
+++ b/docs/src/main/paradox/typed/handling-actor-responses-with-scala3.md
@@ -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
\ No newline at end of file
diff --git a/docs/src/main/paradox/typed/images/adapted-response-scala-3.png
b/docs/src/main/paradox/typed/images/adapted-response-scala-3.png
new file mode 100644
index 0000000000..1e9ec17684
Binary files /dev/null and
b/docs/src/main/paradox/typed/images/adapted-response-scala-3.png differ
diff --git a/docs/src/main/paradox/typed/index.md
b/docs/src/main/paradox/typed/index.md
index e0b096faa8..b162cf62e5 100644
--- a/docs/src/main/paradox/typed/index.md
+++ b/docs/src/main/paradox/typed/index.md
@@ -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)
diff --git a/docs/src/main/paradox/typed/interaction-patterns.md
b/docs/src/main/paradox/typed/interaction-patterns.md
index 6c2506d120..fac3197166 100644
--- a/docs/src/main/paradox/typed/interaction-patterns.md
+++ b/docs/src/main/paradox/typed/interaction-patterns.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:**
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]