I am new to Scala (Akka Actors. I am aware there are benefits of avoiding mutable state in actors, but a found a solution to incrementing a var total similar to that given by the SO question:
Alternatives to using "var" for state with actors?
My code is below.
The solution would appear simple: my Calculator actor maintains a local var total, and the appropriate match on a case class in receive() => increments this total with an amount extracted from the matching case class.
My case statements are working. A println shows that each time my input matches the case statement, the amount to be added to the total (amt) is yielded by the match. I.e. this works as expected:
case MyCaseClass(_, amt) => println(amt)
But this line:
case MyCaseClass(_, amt) => total += amt
fails. The local var total is never incremented. I tried using a list, and adding the new amt as a new member to a var List, but this fails also. In each case increment of the local var, be it type Double or List[Double] fails. Why is this? and what can I do to get the increment to work inside receive()?
Code:
import akka.actor.ActorSystem
import akka.actor.Props
class CostingActor extends Actor {
var bedrooms:Double = 0.0
var bathrooms: Double = 0.0
def receive = {
//THIS IS THE OFFENDING LINE OF CODE--PRINTS OUT P FINE, EACH TIME A MATCH IS MADE
case RoomPojo("Bedroom", p) => bedrooms += p //println(p)
case RoomPojo("Bathroom", p) => bathrooms += p //println(p)
case "total" => println(bedrooms + bathrooms)
}
}
object Main extends App {
val system = ActorSystem("CostingSystem")
var costingActor = system.actorOf(Props[CostingActor], name = "costingactor")
var roomActor = system.actorOf(Props[RoomActor], name = "roomactor")
for (i <- 0 until args.length)
roomActor ! args(i)
//the following line of code may be wrong; (I also tried a future) but should n't the variables bedrooms
// and bathrooms still show at least some incrementation?
costingActor ! "total"
system.shutdown()
}
object RoomActor {
case class RoomPojo(name:String, price:Double) {}
}
class RoomActor extends Actor {
val checkout = context.actorOf(Props[CostingActor])
def receive: Receive = {
case "Bedroom" => checkout ! new RoomPojo("Bedroom", 45.0)
case "Bathroom" => checkout ! new RoomPojo("Bathroom", 90.0)
case _ => println("missed in room")
}
}
The system.actorOf(Props[CostingActor], name = "costingactor")
from your Main
is a different actor than the context.actorOf(Props[CostingActor])
from the RoomActor
. Each actorOf
creates a new instance of this actor, each with its own state. You send the RoomPojo
messages to one actor instance and ask a completely different instance for the state, which hasn't seen any RoomPojo
messages at all.
You can modify the RoomActor to take an actorRef as a parameter and pass the costingActor
to the Props, this way you're using the same actor instance for the state.
var costingActor = system.actorOf(Props[CostingActor], name = "costingactor")
var roomActor = system.actorOf(Props(classOf[RoomActor], costingActor), name = "roomactor")
// ...
class RoomActor(checkout: ActorRef) extends Actor
Also, since everything is asynchronous, the 'total' message might be lost if you shut down the system immediately after sending the message.