Say I have got following two case class
es:
case class Address(street: String, city: String, state: String, zipCode: Int)
case class Person(firstName: String, lastName: String, address: Address)
and the following instance of Person
class:
val raj = Person("Raj", "Shekhar", Address("M Gandhi Marg",
"Mumbai",
"Maharashtra",
411342))
Now if I want to update zipCode
of raj
then I will have to do:
val updatedRaj = raj.copy(address = raj.address.copy(zipCode = raj.address.zipCode + 1))
With more levels of nesting this gets even more uglier. Is there a cleaner way (something like Clojure's update-in
) to update such nested structures?
I've been looking around for what Scala library that has the nicest syntax and the best functionality and one library not mentioned here is monocle which for me has been really good. An example follows:
These are very nice and there are many ways to combine the lenses. Scalaz for example demands a lot of boilerplate and this compiles quick and runs great.
To use them in your project just add this to your dependencies:
Zippers
Huet's Zipper provides convenient traversal and 'mutation' of an immutable data structure. Scalaz provides Zippers for
Stream
(scalaz.Zipper), andTree
(scalaz.TreeLoc). It turns out that the structure of the zipper is automatically derivable from the original data structure, in a manner that resembles symbolic differentiation of an algebraic expression.But how does this help you with your Scala case classes? Well, Lukas Rytz recently prototyped an extension to scalac that would automatically create zippers for annotated case classes. I'll reproduce his example here:
So the community needs to persuade the Scala team that this effort should be continued and integrated into the compiler.
Incidentally, Lukas recently published a version of Pacman, user programmable through a DSL. Doesn't look like he used the modified compiler, though, as I can't see any
@zip
annotations.Tree Rewriting
In other circumstances, you might like to apply some transformation across the entire data structure, according to some strategy (top-down, bottom-up), and based on rules that match against the value at some point in the structure. The classical example is transforming an AST for a language, perhaps to evaluate, simplify, or collect information. Kiama supports Rewriting, see the examples in RewriterTests, and watch this video. Here's a snippet to whet your appetite:
Note that Kiama steps outside the type system to achieve this.
Shapeless does the trick:
with:
Note that whilst some other answers here let you compose lenses to go deeper into a given structure these shapless lenses (and other libraries/macros) let you combine two unrelated lenses such that you can make lens that sets an arbitrary number of parameters into arbitrary positions in your structure. For complex data structures that additional composition is very helpful.
Due to their composable nature, lenses provide a very nice solution to the problem of heavily nested structures. However with a low level of nesting, I sometimes feel lenses are a bit too much, and I don't want to introduce the whole lenses approach if there is only few places with nested updates. For sake of completeness, here is a very simple/pragmatic solution for this case:
What I do is to simply write a few
modify...
helper functions in the top level structure, which deal with the ugly nested copy. For instance:My main goal (simplifying the update on client side) is achieved:
Creating the full set of modify helpers is obviously annoying. But for internal stuff it is often okay to just create them the first time you try to modify a certain nested field.
Useful tools to use Lenses:
Just want to add that the Macrocosm and Rillit projects, based on Scala 2.10 macros, provides Dynamic Lens Creation.
Using Rillit:
Using Macrocosm:
Funny that no one added lenses, since they were MADE for this kind of stuff. So, here is a CS background paper on it, here is a blog which touch briefly on lenses use in Scala, here is a lenses implementation for Scalaz and here is some code using it, which looks surprisingly like your question. And, to cut down on boiler plate, here's a plugin that generate Scalaz lenses for case classes.
For bonus points, here's another S.O. question which touches on lenses, and a paper by Tony Morris.
The big deal about lenses is that they are composable. So they are a bit cumbersome at first, but they keep gaining ground the more you use them. Also, they are great for testability, since you only need to test individual lenses, and can take for granted their composition.
So, based on an implementation provided at the end of this answer, here's how you'd do it with lenses. First, declare lenses to change a zip code in an address, and an address in a person:
Now, compose them to get a lens that changes zipcode in a person:
Finally, use that lens to change raj:
Or, using some syntactic sugar:
Or even:
Here's the simple implementation, taken from Scalaz, used for this example: