# Contravariant

`Contravariant`

describes a parameterized type `F[A]`

that potentially consumes but never produces `A`

values.

Its signature is:

```
trait Invariant[F[_]] {
def invmap[A, B](f: A <=> B): F[A] <=> F[B]
}
trait Contravariant[F[-_]] extends Invariant[F] {
def contramap[A, B](f: B => A): F[A] => F[B]
final def invmap[A, B](f: A <=> B): F[A] <=> F[B] =
Equivalence(contramap(f.from), contramap(f.to))
}
type <=>[A, B] = Equivalence[A, B]
case class Equivalence[A, B](to: A => B, from: B => A)
```

The `contramap`

operator says we can lift a function `B => A`

to a function `F[A]`

to `F[B]`

. If we import `zio.prelude._`

we can use `contramap`

to transform a `F[A]`

to a `F[B]`

with a function `B => A`

.

Notice that the arrows go in the opposite direction here. To transform a `F[A]`

into a `F[B]`

with the `contramap`

operator we provide a function `B => A`

rather than a function `A => B`

like with the `map`

operator.

This can be a little counterintuitive because we are used to primarily working with data types that produce values and transforming their outputs. We will build a better sense for the `contramap`

operator later in this section.

The other thing to notice here is the `-`

that appears in brackets in the definition of `Contravariant`

. This tells the Scala compiler that `F`

is contravariant with respect to the `A`

type parameter.

Doing this improves type inference because the Scala compiler knows that if `A`

is a subtype of `B`

then an `F[B]`

is a subtype of an `F[A]`

, since an `F[B]`

can accept `B`

inputs and every `A`

is a `B`

. It also allows the compiler to check that `A`

really does only appear as an input to `F`

.

Other functional programming libraries don't take advantage of contravariance here and so have to define a `narrow`

operator, which essentially forces users to do this type casting manually.

The law is that the lifting of the function `f`

in `contramap`

can transform `B`

values into `A`

values but cannot otherwise change the structure of `F`

, so using `contramap`

with the identity function is an identity and separately using `contrmap`

with two functions is the same as doing it with the composition of those functions.

```
fa.contramap(identity) === fa
fa.contramap(f).contramap(g) === fa.contramap(f.compose(g)))
```

Examples of data types that are contravariant include functions with respect to their inputs, `ZIO`

with respect to its environment type, and `ZSink`

with respect to its input type.

To get a better sense of what it means for a data type to be contravariant let's look at a `JSONCodec`

.

```
trait JsonCodec[A] {
def decode(json: String): Either[String, A]
def encode(a: A): String
}
```

This data type doesn't have either a `+`

or a `-`

before the `A`

type parameter, indicating that it is invariant with respect to the `A`

type parameter. If we try to make `JsonCodec`

contravariant by adding a `-`

before the `A`

type parameter we get a compilation error telling us that `A`

appears in covariant position in the `decode`

operator.

This is accurate because `A`

does indeed appear as an output of the `decode`

operator whereas it is only supposed to appear as an input to a contravariant type. To fix this we need to break the `JsonCodec`

up into separate `JsonDecoder`

and `JsonEncoder`

types that are covariant and contravariant respectively.

```
trait JsonDecoder[+A] {
def decode(json: String): Either[String, A]
}
trait JsonEncoder[-A] {
def encode(a: A): String
}
trait JsonCodec[A] extends JsonDecoder[A] with JsonEncoder[A]
```

Now we can define a `Contravariant`

instance for the `JsonEncoder`

.

```
trait JsonEncoder[-A] { self =>
def encode(a: A): String
def contramap[B](f: B => A): JsonEncoder[B] =
new JsonEncoder[B] {
def encode(b: B): String =
self.encode(f(b))
}
}
object JsonEncoder {
implicit val JsonEncoderContravariant: Contravariant[JsonEncoder] =
new Contravariant[JsonEncoder] {
def contramap[A, B](f: B => A): JsonEncoder[A] => JsonEncoder[B] =
jsonEncoder => jsonEncoder.contramap(f)
}
}
```

Let's think about what this means.

A `JsonEncoder`

is something that knows how to encode values of type `A`

. It says we can give it any value of type `A`

and it will encode it.

The `contramap`

operator says if we have a function `B => A`

we can transform a `JsonEncode[A]`

into a `JsonEncoder[B]`

. We can do that by taking any `B`

values and transforming them into `A`

values with the function `f`

before sending them to the original encoder.

The pattern works the same way for any contravariant type. The `contramap`

operator lets us adapt the inputs to the data type with a function, for example transforming a sink that writes bytes to a file to a sink that writes strings to a file by providing a function to transform strings to bytes.

While we may be less familiar with it, the `contramap`

operator is quite useful for working with contravariant types and lets us "work backwards" from the input type we need to the input type we have. So it can be useful to implement a `Contravariant`

instance for our own data types just for that.

In addition, some operators in ZIO Prelude are only defined on data types that have a `Contravariant`

instance along with instances of one or more other functional abstractions. So it is useful to define instances of all functional abstractions that are applicable for your own data types so that you can use these operators when you need them.

Finally, if you are writing your own generic code in terms of the abstractions in ZIO Prelude a `Contravariant`

instance can be important to define certain classes of operators.