First of all, you need to abandon the imperative that blocks the mind. Akka wins best when everything is asynchronous. You should never block while waiting for another actor to respond. Instead, you can take a Future[T] object and apply a function to it. This function will be called when the future is done.
Make a simple example: you have a SquareActor that takes an Int and returns its square. The correct way to ask the answer is:
squareActor ? 9 map {result => println(result)
Very important: a block of code with println will not be blocked. Next, the following code will be executed, and your callback method will be called after a while.
Believing that we can begin to use your use case. If I understand correctly, you have a list of participants and a list of integers. You want to send any integer to exactly one actor. Here is the pseudo code:
val actors: List[ActorRef] = //... val numbers: List[Int] = //... val actorsWithArguments: List[(ActorRef, Int)] = actors zip numbers val futuresOfAny: List[Future[Any]] = actorsWithArguments map { case (actor, number) => actor ? number} val futures: List[Future[Int]] = futuresOfAny map {_.mapTo[Int]} val futureOfAllResults: Future[List[Int]] = Future.sequence(futures) val future: Future[Int] = futureOfAllResults map { _.sum}
I intentionally left explicit types to help you execute the code. Release step by step:
actorsWithArguments is a List tuples. Each element contains a pair of actors and a message - (actor1, message1) , (actor2, message2) , ...
futuresOfAny contains a list of Future results for calling actor ? number actor ? number . ? returns a Future[Any] , because: a) the result is not known (yet) and b) Akka does not know what the type of the result will be (response message)
futures strongly typed, since we know that every answer is Int . We just mapTo every Future[Any] to Future[Int]
futureOfAllResults uses a really amazing conversion from List[Future[Int]] to Future[List[Int]] . In other words, we simply turned the list of future results into a single future result with all the elements.
Future fulfills (will be in the future) your result.
If you think this is a lot of code, it was just for educational purposes. Chains and type of inference can do magic:
val future = Future.sequence(actors zip numbers map { case (actor, number) => actor ? number} map { _.mapTo[Int]}) map { _.sum} }
Finally, you got your result. Well, you will be taken to the future. Now, if you want to send this result to another actor, you simply say:
future map {sum => otherActor ! sum} //or even... future map otherActor.!
I highly recommend that you read the chapter on futures in the official documentation to make it all clear.