# Contravariant Monoidal

The `ContravariantMonoidal` type class is for `Contravariant` functors that can define a `product` function and a `unit` function.

``````import cats.Contravariant

trait ContravariantMonoidal[F[_]] extends Contravariant[F] {
def unit: F[Unit]

def product[A, B](fa: F[A], fc: F[B]): F[(A, B)]

def contramap2[A, B, C](fb: F[B], fc: F[C])(f: A => (B, C)): F[A] =
contramap(product(fb, fc))(f)
}
``````

Notice that this allows us to define the `contramap2` function, much like the `map2` function and the `pure` function on the `Applicative` typeclass, but in reverse.

Basically, if you have two contexts `F[B]` and `F[C]` for types `B` and `C`, as well as a way to produce types `B` and `C` simultaneously from a type `A`, then `ContravariantMonoidal` allows you to obtain a context `F[A]` for the type `A`.

Examples of `ContravariantMonoidal` instances are `Eq` and `Const`, but there are also interesting instances for other types.

## Predicates Have `ContravariantMonoidal`

An example application would be the case of predicates. Consider the type,

``````import cats._

import cats.implicits._

case class Predicate[A](run: A => Boolean)
``````

Then, we can exhibit a `ContravariantMonoidal` for `Predicate` by basing it on the `Monoid` for `Boolean` via `&&` as,

``````implicit val contravariantMonoidalPredicate: ContravariantMonoidal[Predicate] =
new ContravariantMonoidal [Predicate] {
def unit: Predicate[Unit] = Predicate[Unit](Function.const(true))

def product[A, B](fa: Predicate[A], fb: Predicate[B]): Predicate[(A, B)] =
Predicate(x => fa.run(x._1) && fb.run(x._2))

def contramap[A, B](fa: Predicate[A])(f: B => A): Predicate[B] =
Predicate(x => fa.run(f(x)))
}
``````

We could have also used `false` and `||`, but the “and” version tends to be a little more convenient for this application.

Just like for `Contravariant`, we can `contramap` to pull `Predicates` back along functions.

``````case class Money(value: Long)
def isEven: Predicate[Long] = Predicate(_ % 2 == 0)

def isEvenMoney: Predicate[Money] = isEven.contramap(_.value)

isEvenMoney.run(Money(55))
// res1: Boolean = false
``````

We can also lift functions contravariantly into the context instead of contramapping repeatedly.

``````def times2Predicate: Predicate[Long] => Predicate[Long] =
ContravariantMonoidal[Predicate].liftContravariant((x: Long) => 2*x)

def liftMoney: Predicate[Long] => Predicate[Money] =
ContravariantMonoidal[Predicate].liftContravariant(_.value)

def trivial = times2Predicate(isEven)
trivial.run(2)
// res2: Boolean = true
trivial.run(5)
// res3: Boolean = true
``````

More interestingly, we can combine multiple predicates using a `contramapN`.

``````case class Transaction(value: Money, payee: String)

def isEvan: Predicate[String] = Predicate(_ == "Evan")

def isGreaterThan50Dollars: Predicate[Money] = liftMoney(Predicate(_ > 50))

def isEvenPaymentToEvanOfMoreThan50 =
(isEvenMoney, isGreaterThan50Dollars, isEvan).contramapN(
(trans: Transaction) => (trans.value, trans.value, trans.payee))

isEvenPaymentToEvanOfMoreThan50.run(Transaction(Money(56), "Evan"))
// res4: Boolean = true
``````