Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add section on using Scala 3 Union types to eliminate msg adapters #741

Merged
merged 1 commit into from
Oct 17, 2023

Conversation

eloots
Copy link
Contributor

@eloots eloots commented Oct 16, 2023

  • Use InteractionPatternsSpec as an example for applying Scala 3's Union types to symplify actor code
    • Remove response message wrapper & adapter
    • Use the union of the actor's public protocol (the Translate message (the only member of the Command ADT) and the possible responses from the backend (messages JobStarted, JobProgress, and JobCompleted): private type CommandAndResponse = Command | Backend.Response
    • Instead of utilising the message adaptor ActorRef in the replyTo field of the message sent to the backend, context.self is used instead
    • The internal (extended) Behavior[CommandAndResponse] is narrowed to Behavior[Command] at creation time

The diffs between the original and the new version:

136d139
<         private final case class WrappedBackendResponse(response: Backend.Response) extends Command
137a141,142
>         private type CommandAndResponse = Command | Backend.Response
>
139,141c144
<           Behaviors.setup[Command] { context =>
<             val backendResponseMapper: ActorRef[Backend.Response] =
<               context.messageAdapter(rsp => WrappedBackendResponse(rsp))
---
>           Behaviors.setup[CommandAndResponse] { context =>
143,144c146,147
<             def active(inProgress: Map[Int, ActorRef[URI]], count: Int): Behavior[Command] = {
<               Behaviors.receiveMessage[Command] {
---
>             def active(inProgress: Map[Int, ActorRef[URI]], count: Int): Behavior[CommandAndResponse] = {
>               Behaviors.receiveMessage[CommandAndResponse] {
147c150
<                   backend ! Backend.StartTranslationJob(taskId, site, backendResponseMapper)
---
>                   backend ! Backend.StartTranslationJob(taskId, site, context.self)
150,162c153,162
<                 case wrapped: WrappedBackendResponse =>
<                   wrapped.response match {
<                     case Backend.JobStarted(taskId) =>
<                       context.log.info("Started {}", taskId)
<                       Behaviors.same
<                     case Backend.JobProgress(taskId, progress) =>
<                       context.log.info2("Progress {}: {}", taskId, progress)
<                       Behaviors.same
<                     case Backend.JobCompleted(taskId, result) =>
<                       context.log.info2("Completed {}: {}", taskId, result)
<                       inProgress(taskId) ! result
<                       active(inProgress - taskId, count)
<                   }
---
>                 case Backend.JobStarted(taskId) =>
>                   context.log.info("Started {}", taskId)
>                   Behaviors.same
>                 case Backend.JobProgress(taskId, progress) =>
>                   context.log.info2("Progress {}: {}", taskId, progress)
>                   Behaviors.same
>                 case Backend.JobCompleted(taskId, result) =>
>                   context.log.info2("Completed {}: {}", taskId, result)
>                   inProgress(taskId) ! result
>                   active(inProgress - taskId, count)
167c167
<           }
---
>           }.narrow

- Use InteractionPatternsSpec as an example for applying Scala 3's
  Union types to symplify actor code
  - Remove response message wrapper & adapter
  - Use the union of the actor's public protocol (the `Translate`
    message (the only member of the `Command` ADT) and the possible
    responses from the backend (messages `JobStarted`, `JobProgress`,
    and `JobCompleted`): `private type CommandAndResponse = Command | Backend.Response`
  - Instead of utilising the message adaptor `ActorRef` in the `replyTo`
    field of the message sent to the backend, `context.self` is used
    instead
  - The internal (extended) `Behavior[CommandAndResponse]` is narrowed
    to `Behavior[Command]` at creation time

The diffs between the original and the new version:

```bash
136d139
<         private final case class WrappedBackendResponse(response: Backend.Response) extends Command
137a141,142
>         private type CommandAndResponse = Command | Backend.Response
>
139,141c144
<           Behaviors.setup[Command] { context =>
<             val backendResponseMapper: ActorRef[Backend.Response] =
<               context.messageAdapter(rsp => WrappedBackendResponse(rsp))
---
>           Behaviors.setup[CommandAndResponse] { context =>
143,144c146,147
<             def active(inProgress: Map[Int, ActorRef[URI]], count: Int): Behavior[Command] = {
<               Behaviors.receiveMessage[Command] {
---
>             def active(inProgress: Map[Int, ActorRef[URI]], count: Int): Behavior[CommandAndResponse] = {
>               Behaviors.receiveMessage[CommandAndResponse] {
147c150
<                   backend ! Backend.StartTranslationJob(taskId, site, backendResponseMapper)
---
>                   backend ! Backend.StartTranslationJob(taskId, site, context.self)
150,162c153,162
<                 case wrapped: WrappedBackendResponse =>
<                   wrapped.response match {
<                     case Backend.JobStarted(taskId) =>
<                       context.log.info("Started {}", taskId)
<                       Behaviors.same
<                     case Backend.JobProgress(taskId, progress) =>
<                       context.log.info2("Progress {}: {}", taskId, progress)
<                       Behaviors.same
<                     case Backend.JobCompleted(taskId, result) =>
<                       context.log.info2("Completed {}: {}", taskId, result)
<                       inProgress(taskId) ! result
<                       active(inProgress - taskId, count)
<                   }
---
>                 case Backend.JobStarted(taskId) =>
>                   context.log.info("Started {}", taskId)
>                   Behaviors.same
>                 case Backend.JobProgress(taskId, progress) =>
>                   context.log.info2("Progress {}: {}", taskId, progress)
>                   Behaviors.same
>                 case Backend.JobCompleted(taskId, result) =>
>                   context.log.info2("Completed {}: {}", taskId, result)
>                   inProgress(taskId) ! result
>                   active(inProgress - taskId, count)
167c167
<           }
---
>           }.narrow
```
@pjfanning
Copy link
Contributor

backport of 0a09ccc

Copy link
Member

@He-Pin He-Pin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lgtm

Copy link
Contributor

@pjfanning pjfanning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@pjfanning pjfanning merged commit 7d7f081 into apache:1.0.x Oct 17, 2023
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants