Partial updates in an immutable World
“…Immutability seems good and like a goal I should aim for. How can I get this in my domain objects without having to provide a multiplicity of constructors to do partial updates ? Is it really that big a deal ? I’ve rarely had to worry about this stuff in Java at all !”
Both fair question, which I’ll try to address in the remainder of this post.
I find the first query on object creation having some relation to having clearly defined idioms and best practises in Scala, with the answer being blurred by the plethora of techniques for creating new object instances in Scala. Cutting to the chase, the appropriate solution to this scenario is to use default and named parameters, (a feature which has been in Scala since v2.8, but blink and you might have missed them). I’ve included a code sample below and this approach, [basically], leverages the fact that the caller is issuing an update to an instantiated class in the appropriate context. This allows for updating of only the attributes that have been explicitly named and passed in to the function. This also encourages the fluent builder style, whereby mutations and operations over the domain object can be chained together to form a pipeline of transformation.
case class Person(val firstName: String, val lastName: String, val age: Int, val email: String) { def update(firstName: String = firstName, lastName: String = lastName, age: Int = age, email: String = email) : Person = { Person(firstName, lastName, age, email) } } val seedPerson = Person('A', 'B', 1, 'me@home.com') println(seedPerson) val updatedPerson = seedPerson update (age = 100, firstName = 'Z') println(updatedPerson)
Note, this isn’t the only approach to performing partial updates, as similar results could also be achieved using a reflection [though at the expense of static type checking support], or using extractors, [which would likely be more verbose due to the lack of context], or even using zippers, tree rewriting or lenses ! (depending on how deep and object graph you need to update, and whether the requirements dictate updating all items in the object graph based on a regex/pattern match).
So, to the question of whether you need immutable data objects. I see this as being a follow up question to the broader issue of whether you actually want or need concurrency at all. Arguably, at different ends of the ‘enterprise‘ spectrum, support for concurrency is not appropriate. For example, in low latency, high transactional systems (where execution is typically single threaded to avoid the cost of context switching) and in single user embedded devices (where resources and interaction levels are constrained) concurrency is eschewed due to the non functional requirements. Also, certain business models do not need to cater for concurrent execution, such as ‘his is partitioned publisher‘ scenarios (where idempotency is important, but not concurrency).
Having said that, (and somewhat tautologically) concurrency problems are generally a problem when they’re a problem. Depending on the profile and semantics of the program, a system could run for some time before any issues arise. However, when you encounter a concurrency bug, like when Jon and Mrs. H met in Hart to Hart, it can be murder. Ultimately, if you’re going to either want or need concurrency guarantees over your domain, immutability is one option and using Scala’s default and named parameters removes much of the boilerplate pain of the implementation. It’s hard to say no to something for [close to] nothing, (something I’d informally termed the L’Oreal principle).
A final observation with this approach is that updating collections still need custom handling to capture the semantics of what the ‘update’ is expected to achieve.
Hopefully this post will help others moving to Scala and trying to tackle the challenges of the functional paradigm.
Happy hacking !
Reference: Partial updates in an immutable World from our JCG partner Kingsley Davies at the Scalabound blog.