Search code examples
scalaakka

How to modify Scala actor to return List[String]?


Following the Akka tutorials https://doc.akka.io/docs/akka/current/typed/guide/tutorial_1.html I have modified sample code to send a message to an akka actor every 3 seconds:

  scheduler.scheduleAtFixedRate(
    initialDelay = Duration(0, TimeUnit.SECONDS),
    interval = Duration(3, TimeUnit.SECONDS))(
    runnable = task)

}

I'm unable to safely compiling changing the message return in Main from String to List[String]. So instead of firstRef ! "printit" , change to firstRef ! List("printit") but this causes compiler error:

enter image description here

To fix the compiler error I make changes to:

class Main(context: ActorContext[String]) extends AbstractBehavior[List[String]](context) {
  override def onMessage(msg: String): Behavior[List[String]] =
def apply(): Behavior[List[String]] =

where previously this contained:

class Main(context: ActorContext[String]) extends AbstractBehavior[String](context) {
  override def onMessage(msg: String): Behavior[String] =
def apply(): Behavior[String] =

What needs to change in order to return List[String] instead of String in the Main actor ?

complete code (without changes):

 import java.util.concurrent.TimeUnit

    import akka.actor.typed.ActorSystem
    import akka.actor.typed.Behavior
    import akka.actor.typed.scaladsl.AbstractBehavior
    import akka.actor.typed.scaladsl.ActorContext
    import akka.actor.typed.scaladsl.Behaviors
    import map.QTableRow

    import scala.concurrent.ExecutionContext
    import scala.concurrent.duration._
    import ExecutionContext.Implicits.global
    import scala.collection.mutable.ListBuffer

    object PrintMyActorRefActor {
      def apply(): Behavior[String] =
        Behaviors.setup(context => new PrintMyActorRefActor(context))
    }

    class PrintMyActorRefActor(context: ActorContext[String]) extends AbstractBehavior[String](context) {

      override def onMessage(msg: String): Behavior[String] =
        msg match {
          case "printit" =>
            val secondRef = context.spawn(Behaviors.empty[String], "second-actor")
            println(s"Second: $secondRef")
            this
        }
    }

    object Main {
      def apply(): Behavior[String] =
        Behaviors.setup(context => new Main(context))

    }

    class Main(context: ActorContext[String]) extends AbstractBehavior[String](context) {
      override def onMessage(msg: String): Behavior[String] =
        msg match {
          case "getdata" =>

            val firstRef = context.spawn(PrintMyActorRefActor(), "first-actor"+String.valueOf(System.currentTimeMillis()))
            println(s"First: $firstRef")

            firstRef ! "printit"
            this
        }
    }

    object ActorHierarchyExperiments extends App {
      val testSystem = ActorSystem(Main(), "testSystem")
      val scheduler = testSystem.scheduler

      val task = new Runnable { def run() {

        testSystem ! "getdata" }

      }

      scheduler.scheduleAtFixedRate(
        initialDelay = Duration(0, TimeUnit.SECONDS),
        interval = Duration(3, TimeUnit.SECONDS))(
        runnable = task)

    }

Solution

  • The firstRef actor reference references an instance of PrintMyActorRefActor, which is a Behavior[ String ], so to send any List[ String ]to the ref, the PrintMyActorRefActor needs to be able to recieve it. Changing this, including the signature of its recieve message, should make the snippet compile. Now, of course, you'll need to change the behaviour of the PrintMyActorRefActor.onMessage( msg: List[ String ] ): Behavior[ List[ String ] ] to deal with the list instead of the single string...

    Complete transformed code snippet:

    import java.util.concurrent.TimeUnit
    
    import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
    import akka.actor.typed.{ActorSystem, Behavior}
    
    import scala.concurrent.ExecutionContext.Implicits.global
    import scala.concurrent.duration._
    
    object PrintMyActorRefActor {
      def apply(): Behavior[List[String]] =
        Behaviors.setup(context => new PrintMyActorRefActor(context))
    }
    
    class PrintMyActorRefActor(context: ActorContext[List[String]]) extends AbstractBehavior[List[String]](context) {
    
      override def onMessage(msg: List[String]): Behavior[List[String]] =
        msg match {
          case "printit" :: xs => // ignores all but the head element
            val secondRef = context.spawn(Behaviors.empty[String], "second-actor")
            println(s"Second: $secondRef")
            this
        }
    }
    
    object Main {
      def apply(): Behavior[String] =
        Behaviors.setup(context => new Main(context))
    
    }
    
    class Main(context: ActorContext[String]) extends AbstractBehavior[String](context) {
      override def onMessage(msg: String): Behavior[String] =
        msg match {
          case "getdata" =>
    
            val firstRef = context.spawn(PrintMyActorRefActor(), "first-actor" + String.valueOf(System.currentTimeMillis()))
            println(s"First: $firstRef")
    
            firstRef ! List("printit")
            this
        }
    }
    
    object ActorHierarchyExperiments extends App {
      val testSystem = ActorSystem(Main(), "testSystem")
      val scheduler = testSystem.scheduler
    
      val task = new Runnable {
        def run() {
          testSystem ! "getdata"
        }
      }
    
      scheduler.scheduleAtFixedRate(
        initialDelay = Duration(0, TimeUnit.SECONDS),
        interval = Duration(3, TimeUnit.SECONDS))(
        runnable = task)
    }