开发者

Creating immutable instances and modifying copies in an idiomatic way

I would like to conditionally create copies of an object instance depending on information external to that instance. Most of the information in the copies will be the same as the original, but some of the information will need to change. This information is being passed around between actors, so I need the objects to be immutable in order to avoid strange concurrency-related behavior. The following toy code is a simple example of what I would like some help with.

If I have the following code:

case class Container(condition:String,amount:Int,ID:Long)

I can do the following:

    val a = new Container("Hello",10,1234567890)
    println("a = " + a)
    val b = a.copy(amount = -5)
    println("b = " + b)
    println("amount in b is " + b.amount)

and the output is

a = Container(Hello,10,1234567890)
b = Container(Hello,-5,1234567890)
amount in b is -5

I can also conditionally create copies of the object doing the following:

开发者_如何学运维
import scala.Math._
val max = 3     
val c = if(abs(b.amount) >= max) b.copy(amount = max,condition="Goodbye") else if(abs(b.amount) < max) b.copy(amount = abs(b.amount))
println("c = " + c)

If I set the amount in the b object to -5, then the output is

c = Container(Goodbye,3,1234567890)

and if I set the amount in the b object to -2, then the output is

c = Container(Hello,2,1234567890)

However, when I try to print out c.amount, it gets flagged by the compiler with the following message

println("amount in c is " + c.amount)

value amount is not a member of Any

If I change the c object creation line to

val c:Container = if(abs(b.amount) >= max) b.copy(amount = max,condition="Goodbye") else if(abs(b.amount) < max) b.copy(amount = abs(b.amount))

I get the compiler error

type mismatch; found: Unit required: Container

What is the best, idiomatic way of conditionally creating immutable instances of case classes by copying existing instances and modifying a value or two?

Thanks, Bruce


You are not including a final else clause. Thus the type of c is Any -- the only type that is supertype both of Container and Unit, where Unit is the result of not including a catch-all else clause. If you try to force the result type to be Container, by writing c: Container =, the compiler now tells you the missing else clause resulting in Unit is not assignable to Container.

Thus

val c = if (abs(b.amount) >= max) {
  b.copy(amount = max, condition = "Goodbye")
} else if (abs(b.amount) < max) {
  b.copy(amount = abs(b.amount))
} else b // leave untouched !

works. The compiler isn't smart enough to figure out that the last else clause cannot be logically reached (it would need to know what abs and >= and < means, that they are mutual exclusive and exhaustive, and that abs is purely functional, as is b.amount).

In other words, since you know that the two clauses are mutually exclusive and exhaustive, you can simplify

val c = if (abs(b.amount) >= max) {
  b.copy(amount = max, condition = "Goodbye")
} else { // i.e. abs(b.amount) < max
  b.copy(amount = abs(b.amount))
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜