Chains - laforge49/Asynchronous-Functional-Programming GitHub Wiki
It can be awkward at time to execute a series of operations.
simpleActor(Prnt(1)) {
rsp1 => {
simpleActor(Prnt(2)) {
rsp2 => {
simpleActor(Prnt(3)) {
rsp3 => {
simpleActor(Prnt("scadoo!"))(rf)
}
}
}
}
}
}
Instead, we can simply create a list of operations and have them run by an actor.
val chain = new Chain
chain.op(simpleActor, Prnt(1))
chain.op(simpleActor, Prnt(2))
chain.op(simpleActor, Prnt(3))
chain.op(simpleActor, Prnt("scadoo!"))
Future(simpleActor, chain)
But things are not always this simple. Quite often we need the results of one operation to create the message used in another.
simpleActor(UltimateAnswer()) {
rsp => {
simpleActor(Prnt(rsp))(rf)
}
}
We can use the Results class to collect results and make them available for subsequent processing. The chain class can then add the result of an operation to the results and also accept a function in place of a message.
val results = new Results
val chain = new Chain(results)
val simpleActor = new SimpleActor
chain.op(simpleActor, UltimateAnswer(), "ultimateAnswer")
chain.op(simpleActor,
Unit => Prnt("The Ultimate Answer to Everything: " + results("ultimateAnswer")
)
The first two parameters of op are functions: Unit => Any. When a parameter is not a function, it is implicitly converted to a function. But to pass a value based on the result of an earlier operation, a function must be passed explicitly. This way the value is not computed until the earlier operations have completed.
If either of the parameters of op evaluate to null, then the operation is skipped. By this means we provide for conditional operation.
When not passed a results object, Chain creates one on its own. Chain also has an apply method for accessing reults. This allows us to simplify the code a little:
val chain = new Chain
val simpleActor = new SimpleActor
chain.op(simpleActor, UltimateAnswer(), "ultimateAnswer")
chain.op(
simpleActor,
Unit => Prnt("The Ultimate Answer to Everything: " + chain("ultimateAnswer"))
)
Future(simpleActor, chain)
We can also bind a message class directly to a method which configures a chain using ChainFactory, which is a subclass of Safe.
case class PrntChain(value: Any)
...
bindSafe(classOf[PrntChain], new ChainFactory(chainFunction))
private def chainFunction(msg: AnyRef, chain: Chain) {
chain.op(this, Prnt(1))
chain.op(this, Prnt(2))
chain.op(this, Prnt(3))
chain.op(this, Prnt("scadoo!"))
}