2

I'm learning about Actors using Scala. I found this great implementation of The Santa Claus Problem here. However, this is using Scala Actors instead of Akka Actors. There is a particular part that I don't understand how to do with Akka Actors, starting at line 80:

/*(line 80)*/object Secretary extends Actor{

  def act = loop(Group[Elf](3), Group[Reindeer](9))

  private def addToGroup[T <% Helper](helper: T, group: Group[T]) = {
    val updatedGroup = group.copy(helpers = helper :: group.helpers)
    if(updatedGroup.hasSpace) updatedGroup else {
      Santa ! updatedGroup
      group.copy(helpers = List[T]())
    }
  }

  // How to implement this using 'receive' with akka
  private def loop(elves: Group[Elf], reindeers: Group[Reindeer]):Unit = {
    react {
      case Idle(reindeer: Reindeer) => loop(elves, addToGroup(reindeer, reindeers))
      case Idle(elf: Elf) => loop(addToGroup(elf, elves), reindeers)
    }
  }

}

I'm using this guide to migrate it to Akka Actors, and I understand that the act method should be replaced with receive. But I don't understand how to do the same loop using receive. An explanation or a sample code would be helpful

apSTRK
  • 477
  • 7
  • 21

1 Answers1

3

You could use context.become like this:

def receive = loop(Group[Elf](3), Group[Reindeer](9))

private def loop(elves: Group[Elf], reindeers: Group[Reindeer]): Receive = {
  case Idle(reindeer: Reindeer) => context become loop(elves, addToGroup(reindeer, reindeers))
  case Idle(elf: Elf) => context become loop(addToGroup(elf, elves), reindeers)
}
senia
  • 37,745
  • 4
  • 88
  • 129
  • This is what I need! Thanks. Just to be clear on how it works: a message is sent and the receive methods gets it and then since both cases in `loop` use become, any new message will be sent to `loop`? @senia – apSTRK Dec 12 '14 at 15:43
  • @apSTRK: Actually it would be better to rename function `loop` to `createBehavior` here. It don't process messages, it creates function that will be used to process messages. Note that `Receive` is the type alias to `PartialFunction[Any, Unit]`, and it is subtype of `Any => Unit`. – senia Dec 12 '14 at 21:18
  • 1
    @apSTRK: `react` method of `scala` actor don't process messages either. It registers listener for next message. So implementations are quite similar. The only difference is function that registers new listener: `react` in `scala` vs `become` in `akka`. Note also that `become` registers listener for any number of new messages, but `react` is only for single message. – senia Dec 12 '14 at 21:39
  • It's starting to make more sense now. I need to make more research to see why Scala is using Akka Actors instead now. One more thing, in `line 66` `Futures` is used. How has that changed with akka? – apSTRK Dec 12 '14 at 21:46
  • @apSTRK: you should not use `await`. You could convert `Seq[Future[T]]` to `Future[Seq[T]]` using `Future.sequence`, then you should combine this future with delayed future using `firstCompletedOf` like in [this answer](http://stackoverflow.com/a/20794853/406435) then you should send message `Continue` to `self` in `onSuccess` of this combined future. Then you should change actors behaviour with `become(waiting, discardOld = false)` to `waiting`: `{ case Continue => {unstashAll(); unbecome()}; case _ => stash() }`. See [this answer](http://stackoverflow.com/a/20591331/406435) for `stash()`. – senia Dec 12 '14 at 22:53
  • @apSTRK: it allows you to preserve all incoming messages without blocking thread (`await` blocks thread and actor should not do so). – senia Dec 12 '14 at 22:57
  • @apSTRK: you can get `Future` from akka actor using ask pattern instead of `!!` – senia Dec 12 '14 at 23:27
  • thanks!I will try all of these. Most of these concepts are new to me, but this is leading me in the right direction – apSTRK Dec 14 '14 at 00:07
  • do you mind explaining what this is doing? `val futureWithTimeout = futures.map{ f => Future firstCompletedOf Seq(f.map{Some(_)}, timeout) }` specifically this part: `f.map{Some(_)} – apSTRK Dec 14 '14 at 21:32
  • @apSTRK: 1. in your case it should be `val futureWithTimeout = Future firstCompletedOf Seq(Future sequence futures, timeout); futureWithTimeout onComplite {_ => self ! Continue}` – senia Dec 14 '14 at 21:38
  • @apSTRK: In that answer: `def getFutureResultAsSomeOrNoneAfterTimeout[T](f: Future[T]): Future[Option[T]] = Future firstCompletedOf Seq(f.map{r => Some(r)}, timeout); val futuresWithTimeout = futures map getFutureResultAsSomeOrNoneAfterTimeout`. – senia Dec 14 '14 at 21:41
  • thanks, I was pretty close. But I'm having the same problem I had before. `Seq(Future sequence futures, timeout)` returns something of type `Seq[Object]` instead of `TraversableOnce[Future[NotInferedT]]`. I'm not sure why this is happening, is it because of how I'm building `futures`? (`val futures: Seq[Future[Any]] = helpers map { _ ask Order }`) Sorry for asking a lot of questions, I'm not sure if I should be creating a new question – apSTRK Dec 14 '14 at 22:45
  • @apSTRK: what is the type of `Future sequence futures` and what is the type of `timeout`? – senia Dec 14 '14 at 22:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/66865/discussion-between-apstrk-and-senia). – apSTRK Dec 14 '14 at 23:03