A simple Scala Quicklens example (functional programming, lenses)

I may explain this more in the future, but for now, here’s some source code for an example of how to use Quicklens in a Scala functional programming project.

Given some model/ADT definitions like this:

case class User(id: Int, name: Name, billingInfo: BillingInfo, phone: String, email: String)
case class Name(firstName: String, lastName: String)
case class Address(street1: String, street2: String, city: String, state: String, zip: String)
case class CreditCard(name: Name, number: String, month: Int, year: Int, cvv: String)
case class BillingInfo(creditCards: Seq[CreditCard])

you can then write some Scala code using Quicklens like this:

object QuicklensExamples extends App {

    val user = User(
        id = 1,
        name = Name(
            firstName = "Al",
            lastName = "Alexander"
        ),
        billingInfo = BillingInfo(
            creditCards = Seq(
                CreditCard(
                    name = Name("Al", "Alexander"),
                    number = "1111111111111111",
                    month = 3,
                    year = 2020,
                    cvv = ""
                )
            )
        ),
        phone = "907-555-1212",
        email = "al@al.com"
    )

    import com.softwaremill.quicklens._

    // (1) how to make simple changes one step at a time
    //val userWithRating = user.modify(_.billingInfo.siteInfo.userRating).using(_ + 1)
    val user1 = user.modify(_.phone).setTo("720-555-1212")
    val user2 = user1.modify(_.email).setTo("al@example.com")
    println(user2)

    // (2) how to update several fields at once
    val u3 = user.modify(_.phone).setTo("720-555-1212")
                 .modify(_.email).setTo("al@example.com")
                 .modify(_.name.firstName).setTo("Alvin")
    println(u3)

}

If you’re not familiar with lenses in functional programming, what they do is let you create updated versions of immutable objects, especially when you need to change values in objects that have nested sub-objects in them. Without lenses you’d have to manually use the Scala case class copy method — and use it a lot. So lenses basically hide a lot of nasty object-copying operations, and make your code look clean in the process.

There are several different “Lens” projects for Scala, and today I’m taking Quicklens out for a short spin.

SBT build.sbt file

Assuming that you’re using SBT, your build.sbt file will need to look something like this:

lazy val root = (project in file(".")).
    settings(
        name := "Quicklens-Example",
        version := "0.1",
        scalaVersion := "2.11.7"
    )

libraryDependencies ++= Seq(
    "com.softwaremill.quicklens" %% "quicklens" % "1.4.0"
)

addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0-M5" cross CrossVersion.full)

Please note that this code is currently a simple modification of this Github project, which is explained at this URL.

Update

As a quick update, I want to note that in regards to the article linked to in the previous paragraph, this is an even more simplified example of what you have to do in FP without lenses:

{
    val newSiteInfo = user.generalInfo.siteInfo.copy(                      //step 1
        userRating = user.generalInfo.siteInfo.userRating + 1
    )
    val newGeneralInfo = user.generalInfo.copy(siteInfo = newSiteInfo)     //step 2
    val newUser = user.copy(generalInfo = newGeneralInfo)                  //step 3
    println("New userRating: " + newUser.generalInfo.siteInfo.userRating)  //print it
}

The end

I’ll write more about this as time permits, but for today I’m posting this code as a “reminder to self” about how to use Quicklens. The Quicklens documentation looks pretty good, but you know, it always helps to see your own code when you’re learning something new.