Send message to parent actor in Akka Typed
Asked Answered
D

2

5

Title is self-explanatory, I want to be able to send a message to a parent actor (meaning I want parent's ActorRef). In Akka Classic (untyped), the ActorRef for a parent actor can be obtained from the child's ActorContext via:

context.parent

(see, for instance, this question (in Java)).

However, the akka.actor.typed.scaladsl.ActorContext in Akka Typed does not expose an ActorRef for the parent. Is there an idiomatic means in Scala to obtain an ActorRef for the parent actor?

Dextroamphetamine answered 7/12, 2020 at 1:40 Comment(11)
Does this answer your question? How to send a message to an actor's parent in Akka? (Java Language)Hourglass
Voting to reopen because the API in question has dramatically changed since the question this was voted a dupe of were made. Akka 2.6 is different enough that an answer which covers pre-2.6 and 2.6 is likely to be unwieldy.Dais
To the OP, are you using the typed actor API? It's worth editing your question to make that clear (it's implied by the old answer not working for you in 2.6, but just to be explicit)Dais
As a further point, the OP's question is in Scala, while the proposed duplicate is Java. In Akka 2.6, especially within the typed API, the general recommendation is to use javadsl or scaladsl depending on the language: thus the best answer for doing something in Java and doing something in Scala are quite possibly going to be different (albeit similar).Dais
@LeviRamsey If the differences between 2.6 and prior version is enough to merit a version specific tags, then tags should be created and applied appropriately. If, however, there's just a better way to do this task now, then the answer should be given on the target instead, so that it can serve as a canonical duplicate target (and so users landing on it have better information to go on).Anaximenes
@Anaximenes replaced the akka-actor tag with akka-typed. Basically, the classic Akka actor API still exists in 2.6 (and the Java in the answers in the target answer also works directly in Scala), but if using the typed API to describe an actor's behavior, no answer in the target would work.Dais
I may be missing something, but seems like your edit to the body and to the tags is putting words in OP's mouth. Why do you think they are using akka-typed? They tagged it with akka-actor, indicating that's what they are using. Typically users don't get to answer a different question than was asked and then change the question to match their answer. That seems to be what has happened here with your edit to the question, adding information about Scala and akka-typed, and your edit to your answer, removing the akka-actor response so that it focuses only on akka-typed.Anaximenes
User's original post mentioned "many outdated answers" not working. Since those outdated answers work if not using the typed API. (note that if following the link for Akka Actor from the official Akka docs takes you to the docs for the typed API doc.akka.io/docs/akka/current/typed/index.html, i.e. if new to Akka, the typed API is akka-actor)Dais
@LeviRamsey Interesting, fair enough. Sounds like the two APIs are not compatible. I'll vote to reopen. However, I do think the question should be taken back to some of its original wording that indicates OP tried older solutions that didn't work for 2.6+.Anaximenes
I'm willing to wait for direct confirmation from the OP that my interpretation of the situation is accurate.Dais
I'll also put an adapted to Java answer regarding the classic and typed in the target question, since that will probably be higher stature.Dais
D
3

If you're in typed Akka, the only [Scala] type that could encompass ActorRefs of all possible parent actors is ActorRef[Nothing], which is an ActorRef you can't send messages to, so that's of limited utility.

At least for as long as the classic APIs exist:

import akka.actor.typed.scaladsl.adapter._

type ClassicActorRef = akka.actor.ActorRef

val parentActorRef = context.toClassic.parent

This will be an untyped ActorRef, i.e. you're free to send messages which the parent actor will never accept.

If you want a typed reference to an actor's parent, you'll need to embed that when spawning the child actor, just as if you want a typed reference to the sender of the current message you need to embed replyTos in your protocol.

(context.sender is absent in the typed ActorContext for the same reason that context.parent is absent; the workaround for replicating classic context.sender is analogous: context.toClassic.sender)

Dais answered 7/12, 2020 at 15:59 Comment(0)
L
5

TLDR: Inject the parent actor reference into the child when creating it.

Akka Typed enforces strict protocols, so you need to make it absolutely clear that "this actor talks to another actor". The accepted answer is a workaround (casting to classic and using the parent), but has its downsides: now you do not enforce types anymore.

Here is some code that should get you started. See how all the types are enforced. You can model the traits differently, but you should get the drift:

object ParentActor {
  sealed trait Command 
  
  case class DoSomething() extends Command
  
  // you do not have to do this, but creating another trait
  // allows you to narrow the amount of messages the parent can receive from the child
  sealed trait ChildNotification extends Command
  case class MessageFromChild() extends ChildNotification

  
  def apply(): Behavior[Command] = {
    Behaviors.receive( (context, message) => 
      message match {
        case DoSomething() =>
          // create a child that knows about its parent
          context.spawn(ChildActor(context.self), "child")
          Behaviors.same

        case MessageFromChild() =>
          context.log.info("I received a message from my child")
          Behaviors.same
      })
  }
}

object ChildActor {
  sealed trait Command
  case class Work() extends Command
  
  // inject the parent here (or any other actor that matches the signature)
  def apply(parent: ActorRef[ParentActor.ChildNotification]): Behavior[Command] = {
     Behaviors.receive( (context, message) => 
       message match {
         case Work() =>
           // send message to parent actor (or any other actor with that type)
           parent ! ParentActor.MessageFromChild()
           Behaviors.same

     })
  }
}

By the way, I am using the "functional" syntax of akka typed, but you can use the more "object-oriented" syntax as well. It follows the same approach.

Leveridge answered 24/2, 2021 at 21:36 Comment(0)
D
3

If you're in typed Akka, the only [Scala] type that could encompass ActorRefs of all possible parent actors is ActorRef[Nothing], which is an ActorRef you can't send messages to, so that's of limited utility.

At least for as long as the classic APIs exist:

import akka.actor.typed.scaladsl.adapter._

type ClassicActorRef = akka.actor.ActorRef

val parentActorRef = context.toClassic.parent

This will be an untyped ActorRef, i.e. you're free to send messages which the parent actor will never accept.

If you want a typed reference to an actor's parent, you'll need to embed that when spawning the child actor, just as if you want a typed reference to the sender of the current message you need to embed replyTos in your protocol.

(context.sender is absent in the typed ActorContext for the same reason that context.parent is absent; the workaround for replicating classic context.sender is analogous: context.toClassic.sender)

Dais answered 7/12, 2020 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.