3

In another language, I have had something call itself with the new state to maintain it but I don't see how to do that with scala actors. Something like this is how I would envision this:

def act(state)
  react {
    case blah => act(doSomething(state, blah))
  }
}
mentics
  • 6,852
  • 5
  • 39
  • 93

2 Answers2

1

Well.. funny thing. I tweak my question a little and find it compiles. I realized that all that happens is that I have to return a partial function. Well I can return a partial function that has a parameter in there, so...

import scala.actors._

object MyActor extends Actor {
  def proc(s: Int) {
    react {
      case input: String =>
        if ((s % 10000) == 0) {
          println(s + ", " + input)
        }
        proc(s + 1)
    }
  }

  def act = proc(0)
}

object TestActors {
  def main(args: Array[String]): Unit = {
    MyActor.start()

    for (s <- 1 to 10000000) {
      MyActor ! "a message"
    }
  }
}

The nice thing is that with a little more tweaking, it could be generalized quite easily.

mentics
  • 6,852
  • 5
  • 39
  • 93
  • 1
    In cases like this it is smart to add the @tailrec annotation to the proc-method (http://stackoverflow.com/questions/3114142/what-is-the-scala-annotation-to-ensure-a-tail-recursive-function-is-optimized). To make sure you're not blowing up your stack. – thoredge Apr 27 '11 at 22:52
  • Thanks. I did run it 1 million times to make sure I wasn't, but having the compiler check that is good. I added it and it gave me an error saying it couldn't optimize it which is a little odd since I would have though 1 million times would have blown up a stack otherwise. – mentics Apr 28 '11 at 01:19
  • 3
    My wrong about the @tailrec annotation here; the proc call is not the last call; react is. Thus it cannot be tail recursive. According to this thread http://scala-programming-language.1934581.n4.nabble.com/scala-Tail-recursion-question-td1990627.html the stack overflow is unlikely to happen. – thoredge Apr 28 '11 at 07:50
  • @thoredge Nice analysis here. – WestCoastProjects May 23 '14 at 15:36
0

There are two other ways to store state in actors without using vars. If you only need to pass state forward, you can have the actor send a message to itself:

object MailsSelf {
  import scala.actors._
  class Selfish extends Reactor[Any] {
    def act() { loop { react {
      case i: Int if (i>=0) =>
        println(i)
        this ! (i-1)  // Self-messaging instead of recursive call
      case _ => exit
    }}}
  }
  def main(args: Array[String]) {
    (new Selfish).start() ! 5
  }
}

Alternatively, if you need to save state that is accessible during other messages, you can create another actor; whoever is calling you then needs to be informed about the new actor:

object EndlessActors {
  import scala.actors._
  class Delegater(n: Int) extends ReplyReactor {
    def act() { loop { react {
      case i: Int if (i*n >= 0) =>
        println(i*n)
        val next = new Delegater(n-1)
        next.start()
        reply(Some(next))
      case _ => reply(None); exit
    }}}
  }
  def main(args: Array[String]) {
    val original = new Delegater(5)
    original.start()
    Iterator.iterate( Option(original) ){ maybe =>
      maybe.flatMap(d => {
        val result = (d !? 5)
        result match {
          case Some(d2: Delegater) => Some(d2)
          case _ => None
        }
      })
    }.takeWhile(_.isDefined).foreach(_ => {})  // Foreach forces evaluation
  }
}

Personally, I think it's silly to do things this way. It's very inefficient (a new actor has to be created and an old one disposed of any time state changes--and actors are not that lightweight!), and it substantially complicates the code. It's more practical in most cases to keep your mutable state safely hidden as private[this] var within the actor, so that you know that only the actor itself may change its mutable state. Alternatively, you can reply not with a new actor but with state information that the caller is supposed to pass back, but this is slightly less safe since they could, in principle, modify the state information rather than just passing it back again.

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • I don't think the first one does what I need. The clients send in inputs, and the actor itself sends itself state--but they are never processed at the same time, so it doesn't have access to state and input at the same time which is the whole point. Also, I don't understand why you say it's shorter--it doesn't appear shorter than anything. And the second one I'm not following and is certainly complex. The solution in the other answer does not have stack overflow issues and does what is desired. I'm not sure what issue you have with it. – mentics Apr 28 '11 at 15:46
  • @taotree - I guess it works okay; I was misinterpreting an overfull mailbox as recursive closure generation. I have changed "two ways" to "two other ways". – Rex Kerr Apr 28 '11 at 16:45