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))
}
精彩评论