 # Invariant

API Documentation: Invariant

The `Invariant` type class is for functors that define an `imap` function with the following type:

``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 ignoring the `g` (or in case of contravariance, `f`) 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` type class, we could use `map` to apply a function `longToDate`:

``````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`:

``````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 type class for this and it is called `Invariant`. Instances of the `Invariant` type class provide the `imap` function:

``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`:

``````import java.util.Date

// import everything for simplicity:
import cats._
import cats.syntax.all._

def longToDate: Long => Date = new Date(_)
def dateToLong: Date => Long = _.getTime

implicit val semigroupDate: Semigroup[Date] =
Semigroup[Long].imap(longToDate)(dateToLong)

val today: Date = longToDate(1449088684104l)
val timeLeft: Date = longToDate(1900918893l)``````
``````today |+| timeLeft
// res1: Date = Thu Dec 24 20:40:02 UTC 2015``````