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:

1 2 3 4 5 6 7 8 9 10 11 12 13 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 ) 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) .

. This can be later used as follows: . 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:

1 2 3 4 5 val modifyAddress = modify ( _: Person ) ( _ . address ) val modifyStreetName = modify ( _: Address ) ( _ . street . name ) val newPerson = ( modifyAddress andThenModify modifyStreetName ) ( person ) . using ( _ . toUpperCase ) 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:

1 copy ( person ) . modifying ( _ . address . street . name ) . using ( _ . toUpperCase ) 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.