A combination of LabelledGeneric
and singleton-typed Symbol
literals supports boilerplate-free lens creation for
arbitrary case classes
// A pair of ordinary case classes ...
case class Address(street: String, city: String, postcode: String)
case class Person(name: String, age: Int, address: Address)
// Some lenses over Person/Address ...
val nameLens = lens[Person] >> Symbol("name")
val ageLens = lens[Person] >> Symbol("age")
val addressLens = lens[Person] >> Symbol("address")
val streetLens = lens[Person] >> Symbol("address") >> Symbol("street")
val cityLens = lens[Person] >> Symbol("address") >> Symbol("city")
val postcodeLens = lens[Person] >> Symbol("address") >> Symbol("postcode")
val person = Person("Joe Grey", 37, Address("Southover Street", "Brighton", "BN2 9UA"))
Read a field
ageLens.get(person) should be(res0)
Update a field
val updatedPerson = ageLens.set(person)(38)
updatedPerson.age should be(res0)
Transform a field
val updatedPerson = ageLens.modify(person)(_ + 1)
updatedPerson.age should be(res0)
Read a nested field
streetLens.get(person) should be(res0)
Update a nested field
val updatedPerson = streetLens.set(person)("Montpelier Road")
updatedPerson.address.street should be(res0)