Scala case class “explicitly exposing the state”

2019-07-22 07:01发布

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 ?

3条回答
Evening l夕情丶
2楼-- · 2019-07-22 07:41

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.

查看更多
爷、活的狠高调
3楼-- · 2019-07-22 07:44

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.

查看更多
老娘就宠你
4楼-- · 2019-07-22 07:59

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.

查看更多
登录 后发表回答