# Reader as a contravariant functor by Mark Seemann

*Any function gives rise to a contravariant functor. An article for object-oriented programmers.*

This article is an instalment in an article series about contravariant functors. It assumes that you've read the introduction. In the first example article, you saw how the Command Handler pattern gives rise to a contravariant functor. The next article gave another example based on predicates.

In the overview article I also mentioned that equivalence and comparison form contravariant functors. Each can be described with an interface, or just function syntax. Let's put them in a table to compare them:

Name |
C# method signature |
C# delegate(s) |
Haskell type(s) |
---|---|---|---|

Command Handler | `void Execute(TCommand command);` |
`Action<TCommand>` |
`a -> ()` `a -> IO ()` |

Specification | `bool IsSatisfiedBy(T candidate);` |
`Predicate<T>` `Func<T, bool>` |
`a -> Bool` |

Equivalence | `bool Equals(T x, T y);` |
`Func<T, T, bool>` |
`a -> a -> Bool` |

Comparison | `int Compare(T x, T y);` |
`Func<T, T, int>` |
`a -> a -> Ordering` |

In some cases, there's more than one possible representation. For example, in C# Predicate is isomorphic to `Func<T, bool>`

. When it comes to the Haskell representation of a Command Handler, the 'direct' translation of `Action<T>`

is `a -> ()`

. In (Safe) Haskell, however, a function with that type is always a no-op. More realistically, a 'handler' function would have the type `a -> IO ()`

in order to allow side effects to happen.

Do you notice a pattern?

### Input variance #

There's a pattern emerging from the above table. Notice that in all the examples, the function types are generic (AKA *parametrically polymorphic*) in their *input* types.

This turns out to be part of a general rule. The actual rule is a little more complicated than that. I'll recommend Sandy Maguire's excellent book Thinking with Types if you're interested in the details.

For first-order functions, you can pick and fix *any* type as the return type and let the input type(s) vary: that function will give rise to a contravariant functor.

In the above table, various handlers fix `void`

(which is isomorphic to *unit* (`()`

) as the output type and let the input type vary. Both Specification and Equivalence fix `bool`

as the output type, and Comparison fix `int`

(or, in Haskell, the more sane type `Ordering`

), and allow the input type to vary.

You can pick any other type. If you fix it as *the* output type for a function and let the input vary, you have the basis for a contravariant functor.

### Reader #

Consider this `IReader`

interface:

public interface IReader<R, A> { A Run(R environment); }

If you fix the environment type `R`

and let the output type `A`

vary, you have a (covariant) functor. If, on the other hand, you fix the *output* type `A`

and allow the input type `R`

to vary, you can have yourself a contravariant functor:

public static IReader<R1, A> ContraMap<R, R1, A>( this IReader<R, A> reader, Func<R1, R> selector) { return new FuncReader<R1, A>(r => reader.Run(selector(r))); }

As an example, you may have this (rather unwarranted) interface implementation:

public sealed class MinutesReader : IReader<int, TimeSpan> { public TimeSpan Run(int environment) { return TimeSpan.FromMinutes(environment); } }

You can fix the output type to `TimeSpan`

and let the input type vary using the `ContraMap`

functions:

[Fact] public void WrappedContravariantExample() { IReader<int, TimeSpan> r = new MinutesReader(); IReader<string, TimeSpan> projected = r.ContraMap((string s) => int.Parse(s)); Assert.Equal(new TimeSpan(0, 21, 0), projected.Run("21")); }

When you `Run`

the `projected`

reader with the input string `"21"`

, the `ContraMap`

function first calls the `selector`

, which (in this case) parses `"21"`

to the integer `21`

. It then calls `Run`

on the 'original' `reader`

with the value `21`

. Since the 'original' `reader`

is a `MinutesReader`

, the output is a `TimeSpan`

value that represents 21 minutes.

### Raw functions #

As was also the case when I introduced the Reader (covariant) functor, the `IReader`

interface is just a teaching device. You don't need the interface in order to turn first-order functions into contravariant functors. It works on raw functions too:

public static Func<R1, A> ContraMap<R, R1, A>(this Func<R, A> func, Func<R1, R> selector) { return r => func(selector(r)); }

In the following I'm going to dispense with the `IReader`

interface and instead work with raw functions.

### Identity law #

A `ContraMap`

method with the right signature isn't enough to be a contravariant functor. It must also obey the contravariant functor laws. As usual, it's proper computer-science work to actually prove this, but you can write some tests to demonstrate the identity law for functions. In this article, you'll see parametrised tests written with xUnit.net. First, the identity law:

[Theory] [InlineData(42)] [InlineData(1337)] [InlineData(2112)] [InlineData(90125)] public void ContravariantIdentityLaw(int input) { Func<int, string> f = i => i.ToString(); Func<int, string> actual = f.ContraMap((int i) => i); Assert.Equal(f(input), actual(input)); }

Here I'm using the `(int i) => i`

lambda expression as the identity function. As usual, you can't easily compare functions for equality, so you'll have to call them to verify that they produce the same output, which they do.

### Composition law #

Like the above example, you can also write a parametrised test that demonstrates that `ContraMap`

obeys the composition law for contravariant functors:

[Theory] [InlineData(4.2)] [InlineData(13.37)] [InlineData(21.12)] [InlineData(901.25)] public void ContravariantCompositionLaw(double input) { Func<string, int> h = s => s.Length; Func<double, TimeSpan> f = i => TimeSpan.FromSeconds(i); Func<TimeSpan, string> g = ts => ts.ToString(); Assert.Equal( h.ContraMap((double d) => g(f(d)))(input), h.ContraMap(g).ContraMap(f)(input)); }

This test defines two local functions, `f`

and `g`

. Once more, you can't directly compare methods for equality, so instead you have to invoke both compositions to verify that they return the same `int`

value.

They do.

### Isomorphisms #

Now that we understand that any first-order function is contravariant, we can see that the previous examples of predicates, handlers, comparisons, and equivalences are really just special cases of the Reader contravariant functor.

For example, `Predicate<T>`

is trivially isomorphic to `Func<T, bool>`

. Still, it might be worthwhile to flesh out how other translations might work:

public static ISpecification<T> AsSpecification<T>(this Predicate<T> predicate) { return new DelegateSpecificationAdapter<T>(predicate); } public static ISpecification<T> AsSpecification<T>(this Func<T, bool> predicate) { return new DelegateSpecificationAdapter<T>(predicate); } private class DelegateSpecificationAdapter<T> : ISpecification<T> { private readonly Predicate<T> predicate; public DelegateSpecificationAdapter(Predicate<T> predicate) { this.predicate = predicate; } public DelegateSpecificationAdapter(Func<T, bool> predicate) : this((Predicate<T>)(x => predicate(x))) { } public bool IsSatisfiedBy(T candidate) { return predicate(candidate); } } public static Predicate<T> AsPredicate<T>(this ISpecification<T> specification) { return candidate => specification.IsSatisfiedBy(candidate); } public static Func<T, bool> AsFunc<T>(this ISpecification<T> specification) { return candidate => specification.IsSatisfiedBy(candidate); }

Above are conversions between `ISpecification<T>`

on the one hand, and `Predicate<T>`

and `Func<T, bool>`

on the other. Not shown are the conversions between `Predicate<T>`

and `Func<T, bool>`

, since they are already built into C#.

Most saliently in this context is that it's possible to convert both `ISpecification<T>`

and `Predicate<T>`

to `Func<T, bool>`

, and `Func<T, bool>`

to `ISpecification<T>`

or `Predicate<T>`

without any loss of information. Specifications and predicates are isomorphic to an open constructed `Func`

- that is, a Reader.

I'll leave the other isomorphisms as exercises, with the following hints:

- You can only convert an
`ICommandHandler<T>`

to a`Func`

if you introduce a`Unit`

value, but you could also try to use`Action<T>`

. - For Equivalence, you'll need to translate the two input arguments to a single object or value.
- The same goes for Comparison.

All the contravariant functor examples shown so far in this article series are isomorphic to the Reader contravariant functor.

Particularly, this also explains why it was possible to make `IEqualityComparer.GetHashCode`

contravariant.

### Haskell #

The Haskell *base* package comes with a Contravariant type class and various instances.

In order to replicate the above `MinutesReader`

example, we can start by implementing a function with equivalent behaviour:

Prelude Data.Functor.Contravariant Data.Time> minutes m = secondsToDiffTime (60 * m) Prelude Data.Functor.Contravariant Data.Time> :t minutes minutes :: Integer -> DiffTime

As GHCi reports, the `minutes`

function has the type `Integer -> DiffTime`

(`DiffTime`

corresponds to .NET's `TimeSpan`

).

The above C# example contramaps a `MinutesReader`

with a function that parses a `string`

to an `int`

. In Haskell, we can use the built-in `read`

function to equivalent effect.

Here's where Haskell gets a little odd. In order to fit the `Contravariant`

type class, we need to flip the type arguments of a function. A normal function is usually written as having the type `a -> b`

, but we can also write it as the type `(->) a b`

. With this notation, `minutes`

has the type `(->) Integer DiffTime`

.

In order to make `minutes`

a contravariant instance, we need to fix `DiffTime`

and let the input vary. What we'd like to have is something like this: `(->) a DiffTime`

. Alas, that's not how you define a legal type class instance in Haskell. We have to flip the types around so that we can partially apply the type. The built-in `newtype Op`

does that:

Prelude Data.Functor.Contravariant Data.Time> :t Op minutes Op minutes :: Op DiffTime Integer

Since the general, partially applied type `Op a`

is a `Contravariant`

instance, it follows that the specific type `Op DiffTime`

is. This means that we can `contramap`

`Op minutes`

with `read`

:

Prelude Data.Functor.Contravariant Data.Time> :t contramap read (Op minutes) contramap read (Op minutes) :: Op DiffTime String

Notice that this maps an `Op DiffTime Integer`

to an `Op DiffTime String`

.

How do you use it?

You can retrieve the function wrapped in `Op`

with the `getOp`

function:

Prelude Data.Functor.Contravariant Data.Time> :t getOp (contramap read (Op minutes)) getOp (contramap read (Op minutes)) :: String -> DiffTime

As you can tell, this expression indicates a `String -> DiffTime`

function. This means that if you call it with a string representation of an integer, you should get a `DiffTime`

value back:

Prelude Data.Functor.Contravariant Data.Time> getOp (contramap read (Op minutes)) "21" 1260s

As usual, this is way too complicated to be immediately useful, but it once again demonstrates that contravariant functors are ubiquitous.

### Conclusion #

Normal first-order functions give rise to contravariant functors. With sufficiently tinted glasses, most programming constructs look like functions. To me, at least, this indicates that a contravariant functor is a fundamental abstraction in programming.

This result looks quite abstract, but future articles will build on it to arrive at a (to me) fascinating conclusion. Until then, though...