-
Notifications
You must be signed in to change notification settings - Fork 1
Loops
Loops are quite common, but difficult to implement asynchronously. It is then fortunate that Functional Programming is becoming more popular, as the best approach to looping is to use actors which implement map, filter, fold, exists and find rather than implementing loops yourself. We will start by looking at simple looping and its limits. Later we will cover the conditional tailrec pattern--it is a bit more complicated but much more powerful.
Simple looping can only be used when (a) each iteration through the loop is independent of the others and (b) the number of iterations is not too large. Effectively, you just generate a message for each iteration and you are done when you have received all the responses. Lets look at some code that prints the numbers from 1 to n.
We will use two types of messages. The first invokes the loop; the second is used for each iteration.
case class Loop(n: Int)
case class I(i: Int)
We will need an actor, P, which prints i when it receives an I message.
class P(mb: Mailbox) extends Actor(mb, null) {
bind (classOf[I], ifunc)
def ifunc(msg: AnyRef, rf: Any => Unit) {println(msg.asInstanceOf[I].i)}
}
OK, now we need the actor, L, which receives a Loop message and sends the I messages to some actor, a.
class L(mb: Mailbox, a: Actor) extends Actor(mb, null) {
bind(classOf[Loop], lfunc)
def lfunc(msg: AnyRef, rf: Any => Unit) {
val n = msg.asInstanceOf[Loop].n
if (n < 1) {
rf(null)
return
}
var i = 0
var r = 0
while (i < n) {
i += 1
a(I(i)) { rsp =>
r += 1
if (r == n) rf(null)
}
}
}
}