Monocle
  • API Docs
  • Optics
  • Release Notes
  • FAQ
  • GitHub

›Documentation

Optics

  • Optics
  • Iso
  • Lens
  • Prism
  • Optional
  • Traversal

Documentation

  • Focus
  • Modules
  • Examples
  • FAQ
  • Release Notes

Focus

Focus is the best starting point into Monocle. Focus lets you define a path within an immutable object. Then, once you reach your desired target, you can just as easily get, replace or modify it. Let’s have a look at the most common use cases.

An important point before we start. Focus is a macro available for both Scala 2.13 and Scala 3. However, the macro API has completely changed between Scala 2 and 3, so for each example, we will first show the version for Scala 3 and then Scala 2 (often more verbose).

Update a field of a case class (Scala 2 & 3)

case class User(name: String, address: Address)
case class Address(streetNumber: Int, streetName: String)

val anna = User("Anna", Address(12, "high street"))
import monocle.syntax.all._

anna
  .focus(_.name)
  .replace("Bob")
// res: User = User(
//   name = "Bob",
//   address = Address(streetNumber = 12, streetName = "high street")
// )

anna
  .focus(_.address.streetNumber)
  .modify(_ + 1)
// res: User = User(
//   name = "Anna",
//   address = Address(streetNumber = 13, streetName = "high street")
// )

Update an optional field of a case class (Scala 3 only)

This time a user may or may not have an Address.

case class User(name: String, address: Option[Address])
case class Address(streetNumber: Int, streetName: String)

val anna = User("Anna", Some(Address(12, "high street")))
val bob  = User("bob" , None)
import monocle.syntax.all._

anna
  .focus(_.address.some.streetNumber)
  .modify(_ + 1)
// res: User = User(
//   name = "Anna",
//   address = Some(value = Address(streetNumber = 13, streetName = "high street"))
// )

bob
  .focus(_.address.some.streetNumber)
  .modify(_ + 1)
// res: User = User(name = "bob", address = None)

As you can see, focusing on the street number has no effect on bob because this instance doesn't have an address.

Update a single element inside a List (Scala 3 only)

In this example, User contains a List of DebitCard. Let's imagine we want to update the expiration date of the second debit card.

import java.time.YearMonth

case class User(name: String, debitCards: List[DebitCard])
case class DebitCard(cardNumber: String, expirationDate: YearMonth, securityCode: Int)

val anna = User(
  "Anna",
  List(
    DebitCard("4568 5794 3109 3087", YearMonth.of(2022, 4), 361),
    DebitCard("5566 2337 3022 2470", YearMonth.of(2024, 8), 990)
  )
)

val bob = User("Bob", List())
import monocle.syntax.all._

anna
  .focus(_.debitCards.index(0).expirationDate)
  .replace(YearMonth.of(2026, 2))
// res: User = User(
//   name = "Anna",
//   debitCards = List(
//     DebitCard(
//       cardNumber = "4568 5794 3109 3087",
//       expirationDate = 2022-04,
//       securityCode = 361
//     ),
//     DebitCard(
//       cardNumber = "5566 2337 3022 2470",
//       expirationDate = 2026-02,
//       securityCode = 990
//     )
//   )
// )

bob
  .focus(_.debitCards.index(1).as[DebitCard].expirationDate)
  .replace(YearMonth.of(2026, 2))
// res: User = User("Bob", List())

replace had no effect on bob because he doesn't have a debit card.

index only targets the object at the specified key. If there is no value at this key, then replace and modify are no-operation.

index also works on other "indexable" datastructures such as Vector or Map.

← TraversalModules →
  • Update a field of a case class (Scala 2 & 3)
  • Update an optional field of a case class (Scala 3 only)
  • Update a single element inside a List (Scala 3 only)

Copyright the maintainers © 2016-2025.