Skip to content

Commit

Permalink
Start focus documentation, add Focus to Scala 2 which redirect to Gen…
Browse files Browse the repository at this point in the history
…Lens (#1073)
  • Loading branch information
julien-truffaut authored Feb 17, 2021
1 parent 834d402 commit 689b2bc
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 8 deletions.
105 changes: 105 additions & 0 deletions docs/src/main/mdoc/focus.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
---
id: focus
title: 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 mdoc:silent
case class User(name: String, address: Address)
case class Address(streetNumber: Int, streetName: String)

val anna = User("Anna", Address(12, "high street"))
```

In Scala 3
```scala
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")
// )
```

The Scala 2 version is the same, except for the import
```scala mdoc
import monocle.macros.syntax.all._

anna
.focus(_.name)
.replace("Bob")

anna
.focus(_.address.streetNumber)
.modify(_ + 1)
```


## Update an optional field of a case class

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

```scala mdoc:reset:silent
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)
```

In Scala 3
```scala
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.

In Scala 2
```scala mdoc
import monocle.Focus
import monocle.macros.syntax.all._

anna
.focus(_.address).some
.andThen(Focus[Address](_.streetNumber))
.modify(_ + 1)

bob
.focus(_.address).some
.andThen(Focus[Address](_.streetNumber))
.modify(_ + 1)
```
6 changes: 3 additions & 3 deletions example/src/test/scala/monocle/LensExample.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class LensMonoExample extends MonocleSuite {
assertEquals(Manual._name.get(john), "John")
assertEquals(Semi.name.get(john), "John")
assertEquals(Person.name.get(john), "John")
assertEquals(john.lens(_.name).get, "John")
assertEquals(john.focus(_.name).get, "John")
}

test("set") {
Expand All @@ -38,14 +38,14 @@ class LensMonoExample extends MonocleSuite {
assertEquals(Manual._age.replace(45)(john), changedJohn)
assertEquals(Semi.age.replace(45)(john), changedJohn)
assertEquals(Person.age.replace(45)(john), changedJohn)
assertEquals(john.lens(_.age).replace(45), changedJohn)
assertEquals(john.focus(_.age).replace(45), changedJohn)
}

test("compose") {
assertEquals((Manual._address andThen Manual._streetNumber).get(john), 126)
assertEquals((Semi.address andThen Semi.streetNumber).get(john), 126)
assertEquals((Person.address andThen Address.streetNumber).get(john), 126)
assertEquals(john.lens(_.address.streetNumber).get, 126)
assertEquals(john.focus(_.address.streetNumber).get, 126)
}

@Lenses("_") // this generates lenses prefixed with _ in the Cat companion object
Expand Down
12 changes: 12 additions & 0 deletions macro/src/main/scala-2.x/monocle/Focus.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package monocle

import monocle.macros.GenLens

object Focus {

/** Focus in Scala 2 is limited to generating Lenses for case classes.
* In Scala 3, the macro is much more powerful and resides in the core module.
*/
def apply[A] = new GenLens[A]

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ trait GenApplyLensSyntax {
}

class GenApplyLensOps[A](private val value: A) extends AnyVal {
@deprecated("use focus", since = "3.0.0-M1")
def lens[C](field: A => C): ApplyLens[A, A, C, C] = macro GenApplyLensOpsImpl.lens_impl[A, C]
def focus[C](field: A => C): ApplyLens[A, A, C, C] = macro GenApplyLensOpsImpl.lens_impl[A, C]
}

class GenApplyLensOpsImpl(val c: blackbox.Context) {
Expand Down
3 changes: 3 additions & 0 deletions macro/src/main/scala-2.x/monocle/macros/syntax/all.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package monocle.macros.syntax

object all extends GenApplyLensSyntax
3 changes: 3 additions & 0 deletions website/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"faq": {
"title": "FAQ"
},
"focus": {
"title": "Focus"
},
"learning_resources": {
"title": "Learning Resources"
},
Expand Down
10 changes: 5 additions & 5 deletions website/pages/en/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ class HomeSplash extends React.Component {
<div className="inner">
<ProjectTitle siteConfig={siteConfig} />
<PromoSection>
<Button href={siteConfig.apiUrl}>API Docs</Button>
<Button href={docUrl("modules", language)}>Documentation</Button>
<Button href={siteConfig.apiUrl}>Scaladoc</Button>
<Button href={docUrl("focus", language)}>Get Started</Button>
<Button href={siteConfig.repoUrl}>View on GitHub</Button>
</PromoSection>
</div>
Expand Down Expand Up @@ -99,13 +99,13 @@ Monocle is a Scala library which offers a simple yet powerful API to access and
\`\`\`scala
import monocle.syntax.all._
val user = User( "Anna", Address(12, "high street"))
val user = User("Anna", Address(12, "high street"))
user.focus(_.name).replace("Bob")
// res: User = User( "Bob", Address(12, "high street"))
// res: User = User("Bob", Address(12, "high street"))
user.focus(_.address.streetName).modify(_.toUpperCase)
// res: User = User( "Anna", Address(12, "HIGH STREET"))
// res: User = User("Anna", Address(12, "HIGH STREET"))
user.focus(_.address.streetNumber).get
// res: Int = 12
Expand Down

0 comments on commit 689b2bc

Please sign in to comment.