Scala "update" immutable object best practices - scala

With a mutable object I can write something like
var user = DAO.getUser(id)
user.name = "John"
user.email ="john#doe.com"
// logic on user
If user is immutable then I need to clone\copy it on every change operation.
I know a few ways to perform this
case class copy
method (like changeName) that creates a new object with the new property
What is the best practice?
And one more question. Is there any existing technique to get "changes" relative to the original object(for example to generate update statement)?

Both ways you've mentioned belongs to functional and OO paradigms respectively. If you prefer functional decomposition with abstract data type, which, in Scala, is represented by case classes, then choose copy method. Using mutators is not a good practice in my option, cause that will pull you back to Java/C#/C++ way of life.
On the other hand making ADT case class like
case class Person(name: String, age: String)
is more consise then:
class Person(_name: String, _age: String) {
var name = _name
var age = _a
def changeName(newName: String): Unit = { name = newName }
// ... and so on
}
(not the best imperative code, can be shorter, but clear).
Of cause there is another way with mutators, just to return a new object on each call:
class Person(val name: String,
val age: String) {
def changeName(newName: String): Unit = new Person(newName, age)
// ... and so on
}
But still case class way is more consise.
And if you go futher, to concurrent/parallel programming, you'll see that functional consept with immutable value are much better, then tring to guess in what state your object currently are.
Update
Thanks to the senia, forgot to mention two things.
Lenses
At the most basic level, lenses are sort of getters and setters for immutable data and looks like this:
case class Lens[A,B](get: A => B, set: (A,B) => A) {
def apply(a: A) = get(a)
// ...
}
That is it. A lens is a an object that contains two functions: get and set. get takes an A and returns a B. set takes an A and B and returns a new A. It’s easy to see that the type B is a value contained in A. When we pass an instance to get we return that value. When we pass an A and a B to set we update the value B in A and return a new A reflecting the change. For convenience the get is aliased to apply. There is a good intro to Scalaz Lens case class
Records
This one, ofcause, comes from the shapeless library and called Records. An implementation of extensible records modelled as HLists of associations. Keys are encoded using singleton types and fully determine the types of their corresponding values (ex from github):
object author extends Field[String]
object title extends Field[String]
object price extends Field[Double]
object inPrint extends Field[Boolean]
val book =
(author -> "Benjamin Pierce") ::
(title -> "Types and Programming Languages") ::
(price -> 44.11) ::
HNil
// Read price field
val currentPrice = book.get(price) // Inferred type is Double
currentPrice == 44.11
// Update price field, relying on static type of currentPrice
val updated = book + (price -> (currentPrice+2.0))
// Add a new field
val extended = updated + (inPrint -> true)

Related

Pattern for generating negative Scalacheck scenarios: Using property based testing to test validation logic in Scala

We are looking for a viable design pattern for building Scalacheck Gen (generators) that can produce both positive and negative test scenarios. This will allow us to run forAll tests to validate functionality (positive cases), and also verify that our case class validation works correctly by failing on all invalid combinations of data.
Making a simple, parameterized Gen that does this on a one-off basis is pretty easy. For example:
def idGen(valid: Boolean = true): Gen[String] = Gen.oneOf(ID.values.toList).map(s => if (valid) s else Gen.oneOf(simpleRandomCode(4), "").sample.get)
With the above, I can get a valid or invalid ID for testing purposes. The valid one, I use to make sure business logic succeeds. The invalid one, I use to make sure our validation logic rejects the case class.
Ok, so -- problem is, on a large scale, this becomes very unwieldly. Let's say I have a data container with, oh, 100 different elements. Generating a "good" one is easy. But now, I want to generate a "bad" one, and furthermore:
I want to generate a bad one for each data element, where a single data element is bad (so at minimum, at least 100 bad instances, testing that each invalid parameter is caught by validation logic).
I want to be able to override specific elements, for instance feeding in a bad ID or a bad "foobar." Whatever that is.
One pattern we can look to for inspiration is apply and copy, which allows us to easily compose new objects while specifying overridden values. For example:
val f = Foo("a", "b") // f: Foo = Foo(a,b)
val t = Foo.unapply(f) // t: Option[(String, String)] = Some((a,b))
Foo(t.get._1, "c") // res0: Foo = Foo(a,c)
Above we see the basic idea of creating a mutating object from the template of another object. This is more easily expressed in Scala as:
val f = someFoo copy(b = "c")
Using this as inspiration we can think about our objectives. A few things to think about:
First, we could define a map or a container of key/values for the data element and generated value. This could be used in place of a tuple to support named value mutation.
Given a container of key/value pairs, we could easily select one (or more) pairs at random and change a value. This supports the objective of generating a data set where one value is altered to create failure.
Given such a container, we can easily create a new object from the invalid collection of values (using either apply() or some other technique).
Alternatively, perhaps we can develop a pattern that uses a tuple and then just apply() it, kind of like the copy method, as long as we can still randomly alter one or more values.
We can probably explore developing a reusable pattern that does something like this:
def thingGen(invalidValueCount: Int): Gen[Thing] = ???
def someTest = forAll(thingGen) { v => invalidV = v.invalidate(1); validate(invalidV) must beFalse }
In the above code, we have a generator thingGen that returns (valid) Things. Then for all instances returned, we invoke a generic method invalidate(count: Int) which will randomly invalidate count values, returning an invalid object. We can then use that to ascertain whether our validation logic works correctly.
This would require defining an invalidate() function that, given a parameter (either by name, or by position) can then replace the identified parameter with a value that is known to be bad. This implies have an "anti-generator" for specific values, for instance, if an ID must be 3 characters, then it knows to create a string that is anything but 3 characters long.
Of course to invalidate a known, single parameter (to inject bad data into a test condition) we can simply use the copy method:
def thingGen(invalidValueCount: Int): Gen[Thing] = ???
def someTest = forAll(thingGen) { v => v2 = v copy(id = "xxx"); validate(v2) must beFalse }
That is the sum of my thinking to date. Am I barking up the wrong tree? Are there good patterns out there that handle this kind of testing? Any commentary or suggestions on how best to approach this problem of testing our validation logic?
We can combine a valid instance and an set of invalid fields (so that every field, if copied, would cause validation failure) to get an invalid object using shapeless library.
Shapeless allows you to represent your class as a list of key-value pairs that are still strongly typed and support some high-level operations, and converting back from this representation to your original class.
In example below I'll be providing an invalid instance for each single field provided
import shapeless._, record._
import shapeless.labelled.FieldType
import shapeless.ops.record.Updater
A detailed intro
Let's pretend we have a data class, and a valid instance of it (we only need one, so it can be hardcoded)
case class User(id: String, name: String, about: String, age: Int) {
def isValid = id.length == 3 && name.nonEmpty && age >= 0
}
val someValidUser = User("oo7", "Frank", "A good guy", 42)
assert(someValidUser.isValid)
We can then define a class to be used for invalid values:
case class BogusUserFields(name: String, id: String, age: Int)
val bogusData = BogusUserFields("", "1234", -5)
Instances of such classes can be provided using ScalaCheck. It's much easier to write a generator where all fields would cause failure. Order of fields doesn't matter, but their names and types do. Here we excluded about from User set of fields so we can do what you asked for (feeding only a subset of fields you want to test)
We then use LabelledGeneric[T] to convert User and BogusUserFields to their corresponding record value (and later we will convert User back)
val userLG = LabelledGeneric[User]
val bogusLG = LabelledGeneric[BogusUserFields]
val validUserRecord = userLG.to(someValidUser)
val bogusRecord = bogusLG.to(bogusData)
Records are lists of key-value pairs, so we can use head to get a single mapping, and the + operator supports adding / replacing field to another record. Let's pick every invalid field into our user one at a time. Also, here's the conversion back in action:
val invalidUser1 = userLG.from(validUserRecord + bogusRecord.head)// invalid name
val invalidUser2 = userLG.from(validUserRecord + bogusRecord.tail.head)// invalid ID
val invalidUser3 = userLG.from(validUserRecord + bogusRecord.tail.tail.head) // invalid age
assert(List(invalidUser1, invalidUser2, invalidUser3).forall(!_.isValid))
Since we basically are applying the same function (validUserRecord + _) to every key-value pair in our bogusRecord, we can also use map operator, except we use it with an unusual - polymorphic - function. We can also easily convert it to List, because every element will be of a same type now.
object polymerge extends Poly1 {
implicit def caseField[K, V](implicit upd: Updater[userLG.Repr, FieldType[K, V]]) =
at[FieldType[K, V]](upd(validUserRecord, _))
}
val allInvalidUsers = bogusRecord.map(polymerge).toList.map(userLG.from)
assert(allInvalidUsers == List(invalidUser1, invalidUser2, invalidUser3))
Generalizing and removing all the boilerplate
Now the whole point of this was that we can generalize it to work for any two arbitrary classes. The encoding of all relationships and operations is a bit cumbersome and it took me a while to get it right with all the implicit not found errors, so I'll skip the details.
class Picks[A, AR <: HList](defaults: A)(implicit lgA: LabelledGeneric.Aux[A, AR]) {
private val defaultsRec = lgA.to(defaults)
object mergeIntoTemplate extends Poly1 {
implicit def caseField[K, V](implicit upd: Updater[AR, FieldType[K, V]]) =
at[FieldType[K, V]](upd(defaultsRec, _))
}
def from[B, BR <: HList, MR <: HList, F <: Poly](options: B)
(implicit
optionsLG: LabelledGeneric.Aux[B, BR],
mapper: ops.hlist.Mapper.Aux[mergeIntoTemplate.type, BR, MR],
toList: ops.hlist.ToTraversable.Aux[MR, List, AR]
) = {
optionsLG.to(options).map(mergeIntoTemplate).toList.map(lgA.from)
}
}
So, here it is in action:
val cp = new Picks(someValidUser)
assert(cp.from(bogusData) == allInvalidUsers)
Unfortunately, you cannot write new Picks(someValidUser).from(bogusData) because implicit for mapper requires a stable identifier. On the other hand, cp instance can be reused with other types:
case class BogusName(name: String)
assert(cp.from(BogusName("")).head == someValidUser.copy(name = ""))
And now it works for all types! And bogus data is required to be any subset of class fields, so it will work even for class itself
case class Address(country: String, city: String, line_1: String, line_2: String) {
def isValid = Seq(country, city, line_1, line_2).forall(_.nonEmpty)
}
val acp = new Picks(Address("Test country", "Test city", "Test line 1", "Test line 2"))
val invalidAddresses = acp.from(Address("", "", "", ""))
assert(invalidAddresses.forall(!_.isValid))
You can see the code running at ScalaFiddle

Scala function to change attribute of class

I created a class Door and want to write a function that changes the open status to whatever is the argument.
The below does not work. What would be the best way of doing that? (Sorry for the beginner question.)
class Door(var name: String,
var open: Boolean){
def open_door(newDoorState: Boolean): Unit = new Door(name, newDoorState)
override def toString(): String =
"(" + name + ", " + newDoorState + ")"
}
var door = new Door("MyDoor",true)
door.name // "MyDoor"
door.open // true
door.open_door(false)
door.name // "MyDoor"
door.open // still true. I would like this to be false
As Łukasz and Venkat Sudheer Reddy Aedama mentioned in their comments, the recommended way to mutate values in Scala is to use immutable objects.
This means that every change of object state you will be creating a new object with updated values. It's necessary for achieving referential transparency allowing programs to be free of side effects, which is considered a desirable property that makes code significantly easier to maintain.
In Scala there is a convenient mechanism called case classes. Let's look at how Door could be defined as a case class:
case class Door(name: String, open: Boolean) {
def opened(open: Boolean) = copy(open = open)
}
As you may have noticed there are is no var or val before class arguments. This is because in case class every argument is assumed to be a val, making case classes immutable by default.
Another difference is that instead of creating a new instance with new operator, it uses a copy method, which allows to change the values of specified fields. It may be hard to see the benefit of doing it this way now, but it becomes very convenient when you are dealing with case classes with lot more fields.
Now you can instantiate Door and update it in the following way:
val door = Door("MyDoor", false)
door.name // "MyDoor"
door.open // false
val door2 = door.opened(true)
door2.name // "MyDoor"
door2.open // true
Another convenience provided by case classes is that you can instantiate them without using new keyword, which is a syntactic sugar for calling Door.apply("MyDoor", false).
So far we have seen how to declare and use a case class for immutable data objects. But does it mean that we should always declare our classes this way?
The important property of case classes is that they are transparent. Which is imposed by the fact that all their fields are exposed as public vals. This is useful when you want to read object's properties the same way as they were stored.
On the other hand, if you don't want to expose the internals of your objects (so your class can be called opaque), for example to enforce some invariants, it might be a better idea to use standard Scala classes.
For general rules and tradeoffs associated with transparency vs opacity I recommend reading Li Haoyi's article Strategic Scala Style: Designing Datatypes.
In some cases, for example if you want to improve performance or reduce memory footprint of your program, you may decide to use mutable class instead:
class Door(var name: String,
var open: Boolean) {
def openDoor(newDoorState: Boolean): Unit =
open = newDoorState
}
val door = new Door("MyDoor",true)
door.name // "MyDoor"
door.open // true
door.openDoor(false)
door.name // "MyDoor"
door.open // false
Just keep in mind that immutable object are generally preferred way of passing the data around, and you should design your programs to be referentially transparent by default, and when it becomes really necessary, then consider such micro-optimizations like changing your data structures to be mutable.
Also I'll add that in Scala there is a convention (that originates from Java) to use camel case for class fields and methods, so instead of naming your method open_door like in the original example, you should name it openDoor.
Your open_door function returns a new door. Instead, it should set the open attribute:
class Door(var name: String, var open: Boolean) {
def openDoor(newDoorState: Boolean) {
open = newDoorState
}
override def toString(): String = "(" + name + ", " + newDoorState + ")"
}
Your current open_door method creates new instance of Door, where open is set to newDoorState.
To achieve what you want, you have to do:
def open_door(newDoorState: Boolean): Unit {
open = newDoorState
}
(Note the absense of equal sign before the function body. Also, you can drop ":Unit" part - it will be inferred.)
Thanks, so far the answers were all object oriented.
I'm adding a more functional answer, i.e. not changing the original Door class:
class Door{
var name="MyDoor"
var open_status=true
def this(name:String, open_status:Boolean){
this()
this.open_status = open_status
}
override def toString = {
"%s is open is %s.".format(name, open_status)
}
}
println(new Door)
println(new Door("MyDoor",false))

scala programming without vars

val and var in scala, the concept is understandable enough, I think.
I wanted to do something like this (java like):
trait PersonInfo {
var name: Option[String] = None
var address: Option[String] = None
// plus another 30 var, for example
}
case class Person() extends PersonInfo
object TestObject {
def main(args: Array[String]): Unit = {
val p = new Person()
p.name = Some("someName")
p.address = Some("someAddress")
}
}
so I can change the name, address, etc...
This works well enough, but the thing is, in my program I end up with everything as vars.
As I understand val are "preferred" in scala. How can val work in this
type of example without having to rewrite all 30+ arguments every time one of them is changed?
That is, I could have
trait PersonInfo {
val name: Option[String]
val address: Option[String]
// plus another 30 val, for example
}
case class Person(name: Option[String]=None, address: Option[String]=None, ...plus another 30.. ) extends PersonInfo
object TestObject {
def main(args: Array[String]): Unit = {
val p = new Person("someName", "someAddress", .....)
// and if I want to change one thing, the address for example
val p2 = new Person("someName", "someOtherAddress", .....)
}
}
Is this the "normal" scala way of doing thing (not withstanding the 22 parameters limit)?
As can be seen, I'm very new to all this.
At first the basic option of Tony K.:
def withName(n : String) = Person(n, address)
looked promising, but I have quite a few classes that extends PersonInfo.
That means in each one I would have to re-implement the defs, lots of typing and cutting and pasting,
just to do something simple.
If I convert the trait PersonInfo to a normal class and put all the defs in it, then
I have the problem of how can I return a Person, not a PersonInfo?
Is there a clever scala thing to somehow implement in the trait or super class and have
all subclasses really extend?
As far as I can see all works very well in scala when the examples are very simple,
2 or 3 parameters, but when you have dozens it becomes very tedious and unworkable.
PersonContext of weirdcanada is I think similar, still thinking about this one. I guess if
I have 43 parameters I would need to breakup into multiple temp classes just to pump
the parameters into Person.
The copy option is also interesting, cryptic but a lot less typing.
Coming from java I was hoping for some clever tricks from scala.
Case classes have a pre-defined copy method which you should use for this.
case class Person(name: String, age: Int)
val mike = Person("Mike", 42)
val newMike = mike.copy(age = 43)
How does this work? copy is just one of the methods (besides equals, hashCode etc) that the compiler writes for you. In this example it is:
def copy(name: String = name, age: Int = age): Person = new Person(name, age)
The values name and age in this method shadow the values in the outer scope. As you can see, default values are provided, so you only need to specify the ones that you want to change. The others default to what there are in the current instance.
The reason for the existence of var in scala is to support mutable state. In some cases, mutable state is truly what you want (e.g. for performance or clarity reasons).
You are correct, though, that there is much evidence and experience behind the encouragement to use immutable state. Things work better on many fronts (concurrency, clarity of reason, etc).
One answer to your question is to provide mutator methods to the class in question that don't actually mutate the state, but instead return a new object with a modified entry:
case class Person(val name : String, val address : String) {
def withName(n : String) = Person(n, address)
...
}
This particular solution does involve coding potentially long parameter lists, but only within the class itself. Users of it get off easy:
val p = Person("Joe", "N St")
val p2 = p.withName("Sam")
...
If you consider the reasons you'd want to mutate state, then thing become clearer. If you are reading data from a database, you could have many reasons for mutating an object:
The database itself changed, and you want to auto-refresh the state of the object in memory
You want to make an update to the database itself
You want to pass an object around and have it mutated by methods all over the place
In the first case, immutable state is easy:
val updatedObj = oldObj.refresh
The second is much more complex, and there are many ways to handle it (including mutable state with dirty field tracking). It pays to look at libraries like Squery, where you can write things in a nice DSL (see http://squeryl.org/inserts-updates-delete.html) and avoid using the direct object mutation altogether.
The final one is the one you generally want to avoid for reasons of complexity. Such things are hard to parallelize, hard to reason about, and lead to all sorts of bugs where one class has a reference to another, but no guarantees about the stability of it. This kind of usage is the one that screams for immutable state of the form we are talking about.
Scala has adopted many paradigms from Functional Programming, one of them being a focus on using objects with immutable state. This means moving away from getters and setters within your classes and instead opting to to do what #Tony K. above has suggested: when you need to change the "state" of an inner object, define a function that will return a new Person object.
Trying to use immutable objects is likely the preferred Scala way.
In regards to the 22 parameter issue, you could create a context class that is passed to the constructor of Person:
case class PersonContext(all: String, of: String, your: String, parameters: Int)
class Person(context: PersonContext) extends PersonInfo { ... }
If you find yourself changing an address often and don't want to have to go through the PersonContext rigamarole, you can define a method:
def addressChanger(person: Person, address: String): Person = {
val contextWithNewAddress = ...
Person(contextWithNewAddress)
}
You could take this even further, and define a method on Person:
class Person(context: PersonContext) extends PersonInfo {
...
def newAddress(address: String): Person = {
addressChanger(this, address)
}
}
In your code, you just need to make remember that when you are updating your objects that you're often getting new objects in return. Once you get used to that concept, it becomes very natural.

Scala: referencing companion object from a child class

I'm thinking of a following Scala class layout. I have a basic trait that represents an Item - an interface of what ought to be an immutable object that we can query for name, weight and do some object-specific stuff by invoking methods like equip:
trait Item {
def name: String
def weight: Int
def equip // ... more abstract methods
}
I can create Item implementations, creating case classes by hand, but I'd like to have some sort of "dictionary"-based items - i.e. a static map holds mapping from type ID to values, name and weight methods just query the dictionary with a stored type ID:
object Weapon {
final val NameDict = Map(1 -> "short sword", 2 -> "long sword")
final val WeightDict = Map(1 -> 15, 2 -> 30)
}
case class Weapon(typ: Int) extends Item {
import Weapon._
def name = NameDict(typ)
def weight = WeightDict(typ)
def equip = ... // implementation for weapon
}
So far, so good. I can use Weapon(1) to reference an item object for "short sword". However, the same basic principle applies to any other item types, such as Armor, which uses exactly the same name and weight implementation, but completely different equip and other abstract methods implementation, for example:
object Armor {
final val NameDict = Map(3 -> "bronze armor", 4 -> "iron armor")
final val WeightDict = Map(3 -> 100, 4 -> 200)
}
case class Armor(typ: Int) extends Item {
import Armor._
def name = NameDict(typ)
def weight = WeightDict(typ)
def equip = ... // implementation for armor
}
Looks pretty similar to Weapon, isn't it? I'd like to factor out the common pattern (i.e. implementation of lookups in companion object dictionaries and common typ value) in something like that:
abstract class DictionaryItem(typ: Int) extends Item {
def name = ???.NameDict(typ)
def weight = ???.WeightDict(typ)
}
object Weapon {
final val NameDict = Map(1 -> "sword", 2 -> "bow")
final val WeightDict = Map(1 -> 15, 2 -> 30)
}
case class Weapon(typ: Int) extends DictionaryItem(typ) {
def equip = ... // implementation for weapon
}
But how do I reference a children's companion object (like Weapon.NameDict) from parent class - i.e. what should I use instead of ???.NameDict(typ) and how do I explain to the compiler that children's companion object must include these dictionaries? Is there a better, more Scala-esque approach to such a problem?
Instead of looking up the names and weights of items in maps, you could make use of instances of your case classes. In fact there are some good reasons you might want to take this approach:
Avoid Complexity: It's simple enough, you might not have needed to post this
question about how inheritance works with companion classes.
Avoid Repetition: Using case classes, you would only have to
define your items once.
Avoid Errors: Using synthetic keys, would require you to
use the correct key in both the name and weight dictionaries. See
item 2.
Write Idiomatic Code: Scala is both object-oriented and functional. In
this case, orient yourself around the Item.
Avoid Overhead: Get all the item's properties with a single lookup.
Here's a different take in which Item plays the starring role:
package object items {
val weapons = Weapon("halbred", 25) :: Weapon("pike", 35) :: Nil
val armor = Armor("plate", 65) :: Armor("chainmail", 40) :: Nil
val dictionary = (weapons ::: armor) map(item => item.name -> item) toMap
}
Notice that the names and weights are packaged in the instances instead of packaged as values in a map. And there are no indexes to manage. However, if you needed an index for some reason you could easily use the item's position in the list of equipment or add a property to the Item trait.
And, you might use it like this:
> items dictionary("halbred") // Weapon(halbred, 25)
> items dictionary("plate") weight // 65
One final note, the final keyword in the Scala word is kind of anachronistic. When you define a val in Scala, it's already immutable: Why are `private val` and `private final val` different?
som-snytt is right - just make the Armor and Weapon objects extend a trait, say, Dictionary which has NameDict and WeightDict members, then have DictionaryItem(typ: Dictionary).
It seems like a pretty convoluted way of doing things though, and it's easy to make mistakes and hard to refactor if you make all your properties lookups by number. The more natural way would be to put the actual objects in the map, something like:
case class Weapon(name: String, weight: Int, equip: ...) extends Item
case class Armor(name: String, weight: Int, equip: ...) extends Item
val ShortSword = Weapon(name = "short sword",
weight = 15,
equip = ...)
val LongSword = Weapon("long sword", 20, ...)
val Weapons = Map(
1 -> ShortSword,
2 -> LongSword
)
so you'd write for example Weapons(1).name rather than Weapon.NameDict(1). Or, you could just write ShortSword.name. Or, if you only need to refer to them via their index, you could define the items directly in the Map.
I think you want object Weapon extends Dictionary. I once called it an Armory, to mean a factory for arms. But any -ory or -ary will do. I would also change the integral typ to an Enumeration. Then define Weapon.apply(kind: Kind) to return a Sword when you Weapon(Kinds.Sword).
This pattern of factory methods on traits to be implemented by companion objects is used by the collections framework, which is educational.

Does Scala have record update syntax for making modified clones of immutable data structures?

In Mercury I can use:
A = B^some_field := SomeValue
to bind A to a copy of B, except that some_field is SomeValue instead of whatever it was in B. I believe the Haskell equivalent is something like:
a = b { some_field = some_value }
Does Scala have something like this for "modifying" immutable values. The alternative seems to be to have a constructor that directly sets every field in the instance, which isn't always ideal (if there are invarients the constructor should be maintaining). Plus it would be really clunky and much more fragile if I had to explicitly pass every other value in the instance I want to have a modified copy of.
I couldn't find anything about this by googling, or in a brief survey of the language reference manual or "Scala By Example" (which I have read start-to-finish, but haven't absorbed all of yet, so it may well be in there).
I can see that this feature could have some weird interactions with Java-style access protection and subclasses though...
If you define your class as a case class, a convenient copy method is generated, and calling it you can specify with named parameters new values for certain fields.
scala> case class Sample(str: String, int: Int)
defined class Sample
scala> val s = Sample("text", 42)
s: Sample = Sample(text,42)
scala> val s2 = s.copy(str = "newText")
s2: Sample = Sample(newText,42)
It even works with polymorphic case classes:
scala> case class Sample[T](t: T, int: Int)
defined class Sample
scala> val s = Sample("text", 42)
s: Sample[java.lang.String] = Sample(text,42)
scala> val s2 = s.copy(t = List(1,2,3), 42)
s2: Sample[List[Int]] = Sample(List(1, 2, 3),42)
Note that s2 has a different type than s.
You can use case classes for this, but you don't have to. Case classes are nothing magical - the modifier case just saves you a lot of typing.
The copy method is realized by the use of named and default parameters. The names are the same as the fields and the defaults are the current values of the fields. Here's an example:
class ClassWithCopy(val field1:String, val field2:Int) {
def copy(field1:String = this.field1, field2:Int = this.field2) = {
new ClassWithCopy(field1,field2);
}
}
You can use this just like the copy method on case classes. Named and default parameters are a very useful feature, and not only for copy methods.
If the object you're planning on modifying is a case class then you can use the autogenerated copy method:
scala> val user = User(2, "Sen")
user: User = User(2,Sen)
scala> val corrected = user.copy(name = "Sean")
corrected: User = User(2,Sean)