Quicklens is a small library which allows to modify deeply nested fields in case classes e.g.: modify(person)(_.address.street.name).using(_.toUpperCase)
, without the need to create dedicated lens objects.
I got some very good feedback on the initial release – thanks! There’s also a spin-off implementation, using a different syntax.
One problem that I anticipated from the beginning and also mentioned in the comments, was that it wasn’t possible to traverse Option
al fields. And that’s of now fixed! Option
s and List
s can be “unwrapped” using the .each
method. For example:
import com.softwaremill.quicklens._
case class Street(name: String)
case class Address(street: Option[Street])
case class Person(addresses: List[Address])
val person = Person(List(
Address(Some(Street("1 Functional Rd."))),
Address(Some(Street("2 Imperative Dr.")))
))
val newPerson = modify(person)(_.addresses.each.street.each.name)
.using(_.toUpperCase)
The .each
can only be used inside modify
. You can add support for your own containers by providing an implicit QuicklensFunctor[C]
with the appropriate C
type parameter (there are default implementations for List
, Vector
and Option
).
Other changes
- added documentation on how to create lenses, that is a modification of a path parametrized by the actual object:
val nameLens = modify(_: Person)(_.address.street.name)
. This can be later used as follows:nameLens(person).using(_.toUpperCase)
. - a dedicated method to set a new value of a field, instead of transforming the old value:
modify(person)(_.address.street.name).setTo("2 Imperative Dr.")
- a
andThenModify
method provided by an implicit conversion to combine two lenses:
val modifyAddress = modify(_: Person)(_.address)
val modifyStreetName = modify(_: Address)(_.street.name)
val newPerson = (modifyAddress andThenModify modifyStreetName)(person)
.using(_.toUpperCase)
Alternate syntax
There were also some comments that the current syntax suggests mutable behaviour (which, of course, is not true – all modifications result in copies of the case classes to be created). An alternative could be:
copy(person).modifying(_.address.street.name).using(_.toUpperCase)
However it is a bit longer than the original. What do you think?
Quicklens is available on GitHub under the Apache2 license.
comments powered by Disqus