I have found myself running into error-prone code; the pattern is this:
final class StateVars(val x:Int, val y:Int)
final class StateVarsMutable(var x:Int, var y:Int) {
// req. case classes & >1 element w/ same ordering
// - can also do similar with shapeless
// def snapshot():StateVars = StateVars.tupled(
// StateVarsMutable.unapply(this).get )
def snapshot() = new StateVars(x, y) // generic
}
In the above an instance StateVarsMutable might be contained within an Actor, but the actor may occasionally send a snapshot of its state with StateVars. It looks like kailuowang's henkan could be used. Without compromising run-time performance, is there a best or common approach to this situation?
Mutability of Immutable Classes
The pattern described in the question is unnecessary.
The case class
in scala solves the problem that the question is trying to address by using persistent data structure patterns.
Modifying the question's class declaration to include "case":
final case class StateVars(x: Int, y: Int)
It is now possible to "mutate" this state by returning new objects that represent the new state:
val initialState = StateVars(0,0)
//Increment x while keeping y the same
val incrementX : StateVars = initialState copy (x = initialState.x + 1)
//Increment y while keeping x the same
val incrementY : StateVars = initialState copy (y = initialState.y + 1)
If you need to continuously maintain a "snapshot" then use a var
:
var snapshot : StateVars = StateVars(0,0)
snapshot = snapshot copy (x = snapshot.x + 1, y = snapshot.y +1)
Within Actors
It is possible to write an Actor
that maintains a "snapshot" using the become
functionality to allow for changing values:
object StateVarsRequest
class StateVarsActor(initialStateVars : StateVars) extends Actor {
private def update(currentSnapshot: StateVars) : Receive = {
case stateVars : StateVars => context become update(stateVars)
case _ : StateVarsRequest => sender ! currentSnapshot
}
override def receive : Receive = update(initialStateVars)
}
The snapshot
can be thought of as a pointer to an immutable class. When a requester receives the snapshot in a message they are not getting the mutable pointer, but rather the immutable data.
So if the snapshot changes after a requester has received their data then the requester's data does not change.