Skip to content

Commit

Permalink
Add documentation for Iso
Browse files Browse the repository at this point in the history
Fix errors and add some smaller hints
  • Loading branch information
justjoheinz committed Oct 26, 2015
1 parent 98dba0b commit 61a5267
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 29 deletions.
4 changes: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ notifications:
on_start: false
email:
- truffaut.julien@gmail.com
script: sbt ++$TRAVIS_SCALA_VERSION test
script:
- sbt ++$TRAVIS_SCALA_VERSION test
- sbt ++$TRAVIS_SCALA_VERSION tut
env:
global:
- secure: lJxaR+9VM96s7rfaigL3K2FbcP3XiZ+AerbgOBOpYi2anRkMPQ91h1g60HYTwEDGJIKybyNWFuXZM8hNtQMCxzxgu7ev1SyRr5Otfzg5ehGqB5UWbSAgpXlqhR5dAonwmqyEEDF2OK+k1fa0N6mIwpQeuUCwrt68kTp4f0K9jMQ=
Expand Down
34 changes: 34 additions & 0 deletions docs/src/main/tut/iso.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
layout: default
title: "Iso"
section: "optics"
scaladoc: "http://julien-truffaut.github.io/Monocle/api/#monocle.PLens"
source: "https://github.com/julien-truffaut/Monocle/blob/master/example/src/main/scala/monocle/example/LensExample.scala"
pageSource: "https://raw.githubusercontent.com/julien-truffaut/Monocle/master/docs/src/main/tut/iso.md"
---

# Iso

An `Iso` is an Optic which converts elements of type `S` into elements of type
`A` without loss.

Consider these two case classes:

```tut:silent
case class Person(name: String, age: Int)
case class Pers(n: String, a: Int)
```

In order to create an `Iso` between `Person` and `Pers` we need to supply two total functions

* get : Person => Pers
* reverseGet : Pers => Person

```tut:silent
import monocle.Iso
val personPrism = Iso[Person, Pers]((p: Person) => Pers(p.name, p.age))((p: Pers) => Person(p.n, p.a))
```

and thereby create a lossless conversion between these two types. Other usages include for example the transformation of different types of physical units.

It is important to understand that the transformation between type `S` and `A` works for any type `S` and `A` and that the functions `get` and `reverseGet` are true inverses to each other.
47 changes: 32 additions & 15 deletions docs/src/main/tut/lens.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ case class Address(streetNumber: Int, streetName: String)
```

We can create a `Lens[Address, Int]` which zoom from an `Address` to its field `streetNumber` by supplying a pair of functions:
* `get: Address => Int`

* `get: Address => Int`
* `set: Int => Address => Address`

```tut:silent
import monocle.Lens
val _streetNumber = Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n))
val _streetNumber = Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n))
```

Once we have a `Lens`, we can use the supplied `get` and `set` functions (nothing fancy!):
Expand All @@ -50,15 +50,32 @@ We can push push the idea even further, with `modifyF` we can update the target

```tut:silent
def neighbors(n: Int): List[Int] =
if(n > 0) List(n - 1, n + 1) else List(n + 1)
if(n > 0) List(n - 1, n + 1) else List(n + 1)
import scalaz.std.list._ // to get Functor[List] instance
```

```tut
_streetNumber.modifyF(neighbors)(address)
_streetNumber.modifyF(neighbors)(Address(135, "High Street"))
```

This would work with any kind of `Functor` and is especially useful in conjunction with asynchronous APIs, where one has the task to update a deeply nested structure (see Lens Composition) with the result of an asynchronous computation:

```tut:silent
import scalaz.std.scalaFuture._
import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits._ // to get Future Functor instance
```

```tut
def updateNumber(n: Int) : Future[Int] = Future.successful ( n + 1)
_streetNumber.modifyF(updateNumber)(address)
```




Most importantly, `Lenses` compose to zoom deeper in a data structure

```tut:silent
Expand All @@ -75,12 +92,12 @@ val _address = Lens[Person, Address](_.address)(a => p => p.copy(address = a))


## Lens Generation
`Lens` creation is rather boiler platy but we developed a few macros to generate them automatically. All macros

`Lens` creation is rather boiler platy but we developed a few macros to generate them automatically. All macros
are defined in a separate module:

```scala
libraryDependencies += "com.github.julien-truffaut" %% "monocle-macro" % ${version}
libraryDependencies += "com.github.julien-truffaut" %% "monocle-macro" % ${version}
```

```tut:silent
Expand All @@ -89,7 +106,7 @@ val _age = GenLens[Person](_.age)
```

`GenLens` can also be used to generate `Lens` several level deep:

```tut
GenLens[Person](_.address.streetName).set("Iffley Road")(john)
```
Expand All @@ -108,23 +125,23 @@ Point.x.get(p)
Point.y.set(0)(p)
```

## Laws
## Laws

```tut:silent
class LensLaws[S, A](lens: Lens[S, A]) {
def getSetLaw(s: S): Boolean =
lens.set(lens.get(s)) == s
def setGetLaw(s: S, a: A): Boolean =
lens.get(lens.set(a)(s)) == a
}
```

`getSetLaw` states that if you `get` a value `A` from `S` and then `set` it back in, the result is an object identical to the original one.
A side effect of this law is that `set` is constraint to only update the `A` it points to, for example it cannot
`getSetLaw` states that if you `get` a value `A` from `S` and then `set` it back in, the result is an object identical to the original one.
A side effect of this law is that `set` is constraint to only update the `A` it points to, for example it cannot
increment a counter or modify another value of type `A`.

`setGetLaw` states that if you `set` a value, you always `get` the same value back. This law guarantees that `set` is
actually updating a value of type `A`.
actually updating a value of type `A`.
24 changes: 12 additions & 12 deletions docs/src/main/tut/prism.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ case object Sunday extends Day

We can define a `Prism` which only selects `Tuesday`
`Tuesday` is a singleton, so it is isomorphic to `Unit` (type with a single inhabitant):

```tut:silent
import monocle.Prism
Expand All @@ -32,21 +32,21 @@ val _tuesday = Prism[Day, Unit]{
case _ => None
}(_ => Tuesday)
```

`_tuesday` can then be used as constructor of `Day`:

```tut
_tuesday.reverseGet(())
```

or as a replacement of pattern matching:

```tut
_tuesday.getOption(Monday)
_tuesday.getOption(Tuesday)
```
Let's have look at `Prism` toward larger types such as `LinkedList`.

Let's have look at `Prism` toward larger types such as `LinkedList`.
A `LinkedList` is recursive data type that either empty or a cons, so we can easily define a `Prism` from a `LinkedList`
to each of the two constructors:

Expand All @@ -62,7 +62,7 @@ def _nil[A] = Prism[LinkedList[A], Unit]{

def _cons[A] = Prism[LinkedList[A], (A, LinkedList[A])]{
case Nil() => None
case Cons(h, t) => Some((h, t))
case Cons(h, t) => Some((h, t))
}{ case (h, t) => Cons(h, t)}
```

Expand Down Expand Up @@ -112,15 +112,15 @@ class PrismLaws[S, A](prism: Prism[S, A]) {
def partialRoundTripOneWayLaw(s: S): Boolean =
prism.getOption(s).fold(true)(prism.reverseGet(_) == s)
def roundTripOtherWayLaw(a: A): Boolean =
prism.getOption(prism.reverseGet(a)) == Some(a)
}
```

The first law states that if a `Prism` matches (i.e. `getOption` returns a `Some`), you can always come back
The first law states that if a `Prism` matches (i.e. `getOption` returns a `Some`), you can always come back
to the original value using `reverseGet`.

The second laws states that starting from an `A`, you can do a complete round trip. This law is equivalent to the
The second laws states that starting from an `A`, you can do a complete round trip. This law is equivalent to the
second law of `Iso`.
9 changes: 8 additions & 1 deletion docs/src/site/optics.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,12 @@ section: "optics"

# Optics

## Convert between two types

Isomorphims and Prims essentially deal with the conversion between two types.

- [Iso](tut/iso.html)
- [Prism](tut/prism.html)

## Handle structured data
- [Lens](tut/lens.html)
- [Prism](tut/prism.html)

0 comments on commit 61a5267

Please sign in to comment.