TL;DR: Quicklens: modify deeply nested fields in case classes, e.g.:modify(person)(_.address.street.name).using(_.toUpperCase)
.
Similar to lenses, but without the actual lens creation.
Lenses are very useful when you have to update a deeply nested field in a hierarchy of case classes. For example if we have:
case class Street(name: String)
case class Address(street: Street)
case class Person(address: Address)
val person = Person(Address(Street("1 Functional Rd.")))
and we’d like to modify the name
of the street (let’s say convert to upper case), we would have to do:
person.copy(
address = person.address.copy(
street = person.address.street.copy(
name = person.address.street.name.toUpperCase
)
)
)
Quite a lot of boilerplate! Plus it’s quite hard to see what we are actually trying to achieve.
One solution is to use lenses and lens composition, which provide a much shorter way to achieve the above. There’s a couple of lens libraries, e.g. Monocle; using it, our example now becomes:
val _name = Lenser[Street](_.name)
val _street = Lenser[Address](_.address)
val _address = Lenser[Person](_.person)
(_address ^|-> _street ^|-> _name).modify(_.toUpperCase)(person)
Lenses can be also created using macro annotations, however that’s IDE-unfriendly. The lens objects (_name
, _street
, _address
) provide a view into a specific field of a case class, and can be composed with each other (plus a couple of extra things!).
What if we just want to update a field? The process of creating the lens objects can then become boilerplate as well.
Using the Quicklens macro we can simplify the original task to:
modify(person)(_.address.street.name).using(_.toUpperCase)
As modify
is a macro, this code is transformed during compile time into the appropriate chain of “copy” invocations (no intermediate objects or actual lenses are created); hence the bytecode is almost identical to the original, “boilerplate” solution.
I think Quicklens can be a good alternative for lens creation if you need just the field updates, without the additional features of full-blown lenses. I’m also not aware of a similar existing implementation. Quicklens is open-source and available on Github under the Apache2 license. Comments welcome!
comments powered by Disqus