-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Documentation for the
Invariant
typeclass
- Loading branch information
1 parent
13960c1
commit 620b971
Showing
1 changed file
with
80 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
--- | ||
layout: default | ||
title: "Invariant" | ||
section: "typeclasses" | ||
source: "https://github.com/non/cats/blob/master/core/src/main/scala/cats/functor/Invariant.scala" | ||
scaladoc: "#cats.functor.Invariant" | ||
--- | ||
# Invariant | ||
|
||
The `Invariant` typeclass is for functors that define an `imap` | ||
function with the following type: | ||
|
||
```scala | ||
def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] | ||
``` | ||
|
||
Every covariant (as well as contravariant) functor gives rise to an invariant | ||
functor, by ignorig the `f` (or in case of contravariance, `g`) function. | ||
|
||
Examples for instances of `Invariant` are `Semigroup` and `Monoid`, in | ||
the following we will explain why this is the case using `Semigroup`, the | ||
reasoning for `Monoid` is analogous. | ||
|
||
## Invariant instance for Semigroup | ||
|
||
Pretend that we have a `Semigroup[Long]` representing a standard UNIX | ||
timestamp. Let's say that we want to create a `Semigroup[Date]`, by | ||
*reusing* `Semigroup[Long]`. | ||
|
||
### Semigroup does not form a covariant functor | ||
|
||
If `Semigroup` had an instance for the standard covariant `Functor` | ||
typeclass, we could use `map` to apply a function `longToDate`: | ||
|
||
```tut:silent | ||
import java.util.Date | ||
def longToDate: Long => Date = new Date(_) | ||
``` | ||
|
||
But is this enough to give us a `Semigroup[Date]`? The answer is no, | ||
unfortunately. A `Semigroup[Date]` should be able to combine two | ||
values of type `Date`, given a `Semigroup` that only knows how to | ||
combine `Long`s! The `longToDate` function does not help at all, | ||
because it only allows us to convert a `Long` into a `Date`. Seems | ||
like we can't have an `Functor` instance for `Semigroup`. | ||
|
||
### Semigroup does not form a contravariant functor | ||
|
||
On the other side, if `Semigroup` would form a *contravariant* functor | ||
by having an instance for `Contravariant`, we could make use of | ||
`contramap` to apply a function `dateToLong`: | ||
|
||
```tut:silent | ||
import java.util.Date | ||
def dateToLong: Date => Long = _.getTime | ||
``` | ||
|
||
Again we are faced with a problem when trying to get a | ||
`Semigroup[Date]` based on a `Semigroup[Long]`. As before consider | ||
the case where we have two values of `Date` at hand. Using | ||
`dateToLong` we can turn them into `Long`s and use `Semigroup[Long]` | ||
to combine the two values. We are left with a value of type `Long`, | ||
but we can't turn it back into a `Date` using only `contramap`! | ||
|
||
### Semigroup does form an invariant functor | ||
|
||
From the previous discussion we conclude that we need both the `map` | ||
from (covariant) `Functor` and `contramap` from `Contravariant`. | ||
There already is a typeclass for this and it is called `Invariant`. | ||
Instances of the `Invariant` typeclass provide the `imap` function: | ||
|
||
```scala | ||
def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B] | ||
``` | ||
|
||
Reusing the example of turning `Semigroup[Long]` into | ||
`Semigroup[Date]`, we can use the `g` parameter to turn `Date` into a | ||
`Long`, combine our two values using `Semigroup[Long]` and then | ||
convert the result back into a `Date` using the `f` parameter of | ||
`imap`. |