Last active
June 25, 2017 22:25
-
-
Save richashworth/44ec843872437ce6c10539fde42df51f to your computer and use it in GitHub Desktop.
Lenses example using Monocle http://richashworth.com/2017/06/scrap-the-boilerplate-in-scala-with-lenses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import $ivy.`com.github.julien-truffaut::monocle-core:1.4.0`, monocle.Lens | |
import $ivy.`com.github.julien-truffaut::monocle-macro:1.4.0`, monocle.macros.GenLens | |
// _.copy fine for simple cases | |
case class Address(line1: String, line2: String, postcode: String) | |
val a = Address("221B", "Baker St", "NW1 6XE") | |
println(a) | |
println(a.copy(line1 = "221A")) | |
// Becomes more tedious for nested classes | |
case class Account(ID: Int, owner: Person) | |
case class Person(name: String, address: Address) | |
val p = Person("Sherlock Holmes", a) | |
val acc1 = Account(1, p) | |
val acc2 = acc1.copy( | |
owner = acc1.owner.copy( | |
address = acc1.owner.address.copy( | |
line1 = "221A"))) | |
println(acc2) | |
// Enter Lenses | |
val owner: Lens[Account, Person] = GenLens[Account](_.owner) | |
val address: Lens[Person, Address] = GenLens[Person](_.address) | |
val line1: Lens[Address, String] = GenLens[Address](_.line1) | |
println(owner.get(acc1)) | |
println(line1.set("221A")(a)) | |
// Real power lies in composing lenses | |
val lens: Lens[Account, String] = owner composeLens address composeLens line1 | |
println(lens.get(acc1)) | |
println(lens.set("221A")(acc1)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import $ivy.`com.github.julien-truffaut::monocle-core:1.4.0`, monocle.{Lens, Optional} | |
import $ivy.`com.github.julien-truffaut::monocle-macro:1.4.0`, monocle.macros.GenLens | |
import $file.Lenses, Lenses.Address | |
// Change our domain model so that account owners do not need to provide an address | |
case class Person(name: String, maybeAddress: Option[Address]) | |
case class Account(ID: Int, owner:Person) | |
// Set up some example data | |
val a = Address("221B", "Baker St", "NW1 6XE") | |
val p1 = Person("John Watson", None) | |
val p2 = Person("Sherlock Holmes", Some(a)) | |
val acc = Account(1, p1) | |
// As before, we can define a lens between account and its owner | |
val owner = GenLens[Account](_.owner) | |
// Use `Optional` to manipulate fields of type Option[_]. | |
// Takes two functions as arguments: a 'getter' and a 'setter' | |
val address = Optional[Person, Address](_.maybeAddress)(a => _.copy(maybeAddress = Some(a))) | |
println(address.getOption(p1)) | |
println(address.getOption(p2)) | |
println(address.set(a)(p1)) | |
// Lenses and Optionals compose | |
val optional: Optional[Account, Address] = owner composeOptional address | |
println(optional.getOption(acc)) | |
val updatedAcc = optional.set(a)(acc) | |
println(updatedAcc) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment