Code Monkey home page Code Monkey logo

quicklens's Introduction

Quicklens

Maven Central

Modify deeply nested fields in case classes:

import com.softwaremill.quicklens._

case class Street(name: String)
case class Address(street: Street)
case class Person(address: Address, age: Int)

val person = Person(Address(Street("1 Functional Rd.")), 35)

val p2 = person.modify(_.address.street.name).using(_.toUpperCase)
val p3 = person.modify(_.address.street.name).setTo("3 OO Ln.")

// or
 
val p4 = modify(person)(_.address.street.name).using(_.toUpperCase)
val p5 = modify(person)(_.address.street.name).setTo("3 OO Ln.")

Chain modifications:

person
  .modify(_.address.street.name).using(_.toUpperCase)
  .modify(_.age).using(_ - 1)

Modify conditionally:

person.modify(_.address.street.name).setToIfDefined(Some("3 00 Ln."))
person.modify(_.address.street.name).setToIf(shouldChangeAddress)("3 00 Ln.")

Modify several fields in one go:

import com.softwaremill.quicklens._

case class Person(firstName: String, middleName: Option[String], lastName: String)

val person = Person("john", Some("steve"), "smith")

person.modifyAll(_.firstName, _.middleName.each, _.lastName).using(_.capitalize)

Traverse options/lists/maps using .each:

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 p2 = person.modify(_.addresses.each.street.each.name).using(_.toUpperCase)

.each can only be used inside a modify and "unwraps" the container (currently supports Lists, Options and Mapss - only values are unwrapped for maps). You can add support for your own containers by providing an implicit QuicklensFunctor[C] with the appropriate C type parameter.

Traverse selected elements using .eachWhere:

Similarly to .each, you can use .eachWhere(p) where p is a predicate to modify only the elements which satisfy the condition. All other elements remain unchanged.

def filterAddress: Address => Boolean = ???
person
  .modify(_.addresses.eachWhere(filterAddress)
           .street.eachWhere(_.name.startsWith("1")).name)
  .using(_.toUpperCase)

Modify specific sequence elements using .at:

person.modify(_.addresses.at(2).street.each.name).using(_.toUpperCase)

Similarly to .each, .at modifies only the element at the given index. If there's no element at that index, an IndexOutOfBoundsException is thrown.

Modify specific map elements using .at:

case class Property(value: String)

case class Person(name: String, props: Map[String, Property])

val person = Person(
  "Joe",
  Map("Role" -> Property("Programmmer"), "Age" -> Property("45"))
)

person.modify(_.props.at("Age").value).setTo("45")

Similarly to .each, .at modifies only the element with the given key. If there's no such element, an NoSuchElementException is thrown.

Modify Either fields using .eachLeft and eachRight:

case class AuthContext(token: String)
case class AuthRequest(url: String)
case class Resource(auth: Either[AuthContext, AuthRequest])

val devResource = Resource(auth = Left(AuthContext("fake"))

val prodResource = devResource.modify(_.auth.eachLeft.token).setTo("real")

Modify fields when they are of a certain subtype:

trait Animal
case class Dog(age: Int) extends Animal
case class Cat(ages: List[Int]) extends Animal

case class Zoo(animals: List[Animal])

val zoo = Zoo(List(Dog(4), Cat(List(3, 12, 13))))

val olderZoo = zoo.modifyAll(
  _.animals.each.when[Dog].age,
  _.animals.each.when[Cat].ages.at(0)
).using(_ + 1)

This is also known as a prism, see e.g. here.

Re-usable modifications (lenses):

import com.softwaremill.quicklens._

val modifyStreetName = modify(_: Person)(_.address.street.name)

val p3 = modifyStreetName(person).using(_.toUpperCase)
val p4 = modifyStreetName(anotherPerson).using(_.toLowerCase)

//

val upperCaseStreetName = modify(_: Person)(_.address.street.name).using(_.toUpperCase)

val p5 = upperCaseStreetName(person)

Alternate syntax:

import com.softwaremill.quicklens._

val modifyStreetName = modify[Person](_.address.street.name)

val p3 = modifyStreetName.using(_.toUpperCase)(person)
val p4 = modifyStreetName.using(_.toLowerCase)(anotherPerson)

//

val upperCaseStreetName = modify[Person](_.address.street.name).using(_.toUpperCase)

val p5 = upperCaseStreetName(person)

Composing lenses:

import com.softwaremill.quicklens._

val modifyAddress = modify(_: Person)(_.address)
val modifyStreetName = modify(_: Address)(_.street.name)

val p6 = (modifyAddress andThenModify modifyStreetName)(person).using(_.toUpperCase)

or, with alternate syntax:

import com.softwaremill.quicklens._

val modifyAddress = modify[Person](_.address)
val modifyStreetName = modify[Address](_.street.name)

val p6 = (modifyAddress andThenModify modifyStreetName).using(_.toUpperCase)(person)

Modify nested sealed hierarchies:

Note: this feature is experimental and might not work due to compilation order issues. See https://issues.scala-lang.org/browse/SI-7046 for more details.

import com.softwaremill.quicklens._

sealed trait Pet { def name: String }
case class Fish(name: String) extends Pet
sealed trait LeggedPet extends Pet
case class Cat(name: String) extends LeggedPet
case class Dog(name: String) extends LeggedPet

val pets = List[Pet](
  Fish("Finn"), Cat("Catia"), Dog("Douglas")
)

val juniorPets = pets.modify(_.each.name).using(_ + ", Jr.")

Similar to lenses (1, 2), but without the actual lens creation.

Read the blog for more info.

Available in Maven Central:

val quicklens = "com.softwaremill.quicklens" %% "quicklens" % "1.4.12"

Also available for Scala.js and Scala Native!

quicklens's People

Contributors

adamw avatar ondrejspanel avatar aryairani avatar stanch avatar jozic avatar lolgab avatar gweinbach avatar konradwudkowski avatar slandelle avatar jpapro avatar iamorchid avatar xuwei-k avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.