Created
February 7, 2019 20:37
-
-
Save nrinaudo/838731cabed453bf73fd8160b2147d9b to your computer and use it in GitHub Desktop.
Using optics to patch `Gen` instances
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 monocle.macros._ | |
import org.scalacheck._ | |
import scalaz.scalacheck.ScalaCheckBinding._ | |
// Deep hierarchy of product types. | |
case class Document(author: Author) | |
case class Author(firstName: String, lastName: String, city: City) | |
case class City(name: String, country: Country) | |
case class Country(name: String, continent: Continent) | |
case class Continent(name: String) | |
object GenLenses { | |
// (Stupidly) simple `Gen` instance | |
val genDoc: Gen[Document] = for { | |
firstName <- Gen.identifier | |
lastName <- Gen.identifier | |
city <- Gen.identifier | |
country <- Gen.identifier | |
continent <- Gen.identifier | |
} yield Document(Author(firstName, lastName, City(city, Country(country, Continent(continent))))) | |
// This is how you modify the way the continent's name is generated. | |
val noLens = genDoc.flatMap(doc => doc.copy( | |
author = doc.author.copy( | |
city = doc.author.city.copy( | |
country = doc.author.city.country.copy( | |
continent = doc.author.city.country.continent.copy( | |
name = doc.author.city.country.continent.name.toUpperCase | |
) | |
) | |
) | |
) | |
)) | |
// Same, but with lenses. | |
val continentNameLens = GenLens[Document](_.author.city.country.continent.name) | |
val withLens = genDoc.map(continentNameLens.modify(_.toUpperCase)) | |
// This is how you replace the `Gen` used for the continent name using lenses. | |
val genContinentName = Gen.numStr | |
genDoc.flatMap(doc => genContinentName.map(name => continentNameLens.set(name)(doc))) | |
// Same thing, slightly terser. | |
genDoc.flatMap(continentNameLens.modifyF(_ => genContinentName)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you want to instead "perturb" the continent generator inside, you might write:
whereas the modify-restricted approach would be:
... well, I expected that second one to be longer