While reading akka docs right under messages and immutability section, It mentions about "explicitly exposing the state" inside case class. So my questions are;
What is meant by saying "explicitly exposing the state" for case class?
In order to achieve immutability, isn't it enough to write "case" for a class?
Or I should be careful about its usage ?
What is meant by saying "explicitly exposing the state" for case class?
The actor below represents its state with a mutable Set[Int]
, which is initialized with the values 1
, 2
, and 3
:
case class State(s: mutable.Set[Int])
case class Add(num: Int)
case class Remove(num: Int)
class MyActor extends Actor {
val state = mutable.Set(1, 2, 3)
def receive = {
case GetState =>
sender ! State(state)
case Add(i) =>
state += i
case Remove(i) =>
state -= i
}
}
When this actor receives a GetState
message, it wraps its state in the State
case class and sends that to the sender. Even though the State
case class is immutable, its parameter s
is a mutable Set
. Therefore, when MyActor
creates a State
instance with its state and sends that as a message to the sender of the GetState
message, MyActor
's state becomes modifiable outside of the boundaries of MyActor
itself. To make this concrete, let's say AnotherActor
sends a GetState
message to MyActor
, at which point MyActor
sends its state to AnotherActor
. Here is the latter actor:
class AnotherActor extends Actor {
def receive =>
case State(state) =>
// MyActor's state is exposed here
state -= 2
}
AnotherActor
modifies MyActor
's state by removing 2
from it, even though that state is delivered inside a case class.
To mitigate this sort of leak, restrict mutability to the actor itself. In this example, instead of having a val state = mutable.Set(1, 2, 3)
, define a var state = immutable.Set(1, 2, 3)
:
class MyActor extends Actor {
var state = immutable.Set(1, 2, 3)
def receive = {
case GetState =>
sender ! state
case Add(i) =>
state = state + i
case Remove(i) =>
state = state - i
}
}
Here, MyActor
can safely send its state as a message because it's an immutable Set
(we could wrap the Set
inside a case class, but that's not essential in this case).
In order to achieve immutability, isn't it enough to write "case" for a class?
No. When using a case class for actor messaging, ensure that all of the class's parameters are themselves immutable.
Or I should be careful about its usage?
Yes.
I believe it refers to doing something like
case class Broken(m: mutable.Map[String, String])
Broken
is superficially immutable, because none of its fields can be directly set to anything, but it can still be altered by updating m
, which will cause the behavior of the containing Broken
to change (hashCode
, equals
, etc.), thereby breaking anything that relies on those being consistent. m
corresponds to Broken
's internal (mutable) state, and exposing it allows Broken
to be mutated, too.
I presume you're referring to this particular quote:
Scala case classes which are immutable (if you don’t explicitly expose
the state)
In their simplest form, a case class constructor's parameters will be mapped to 'val' fields and hence their reference will be immutable. However if the field itself is mutable (e.g. referencing a mutable collection or object) and you expose that (via an accessor method, say), then the class will become mutable (since you've exposed a mutable field). Not that you can still change the state internally, however, if you so wish.