# Reader as a profunctor by Mark Seemann

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

This article is an instalment in a short article series about profunctors. It assumes that you've read the introduction.

Previous articles in the overall article series on functors and similar abstractions discussed Reader as both a covariant functor, as well as a contravariant functor. As the profunctor introduction intimated, if you combine the properties of both co- and contravariance, you'll have a profunctor.

There's a wide selection of well-known functors and a smaller selection of contravariant functors. Of all those that I've covered so far, only one appears in both lists: Reader.

### Reader #

Consider, again, this `IReader`

interface:

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

When discussing `IReader`

as a covariant functor, we'd fix `R`

and let `A`

vary. When discussing the same interface as a contravariant functor, we'd fix `A`

and let `R`

vary. If you allow both to vary freely, you have a profunctor.

As the profunctor overview article asserted, you can implement `ContraMap`

and `Select`

with `DiMap`

, or you can implement `DiMap`

with `ContraMap`

and `Select`

. Since previous articles have supplied both `Select`

and `ContraMap`

for `IReader`

, it's a natural opportunity to see how to implement `DiMap`

:

public static IReader<R1, A1> DiMap<R, R1, A, A1>( this IReader<R, A> reader, Func<R1, R> contraSelector, Func<A, A1> coSelector) { return reader.ContraMap(contraSelector).Select(coSelector); }

You simply pass `contraSelector`

to `ContraMap`

and `coSelector`

to `Select`

, while chaining the two method calls. You can also flip the order in which you call these two functions - as long as they are both pure functions, it'll make no difference. You'll shortly see an example of flipping the order.

First, though, an example of using `DiMap`

. Imagine that you have this `IReader`

implementation:

public sealed class TotalSecondsReader : IReader<TimeSpan, double> { public double Run(TimeSpan environment) { return environment.TotalSeconds; } }

This class converts a `TimeSpan`

value into the total number of seconds represented by that duration. You can project this Reader in both directions using `DiMap`

:

[Fact] public void ReaderDiMapExample() { IReader<TimeSpan, double> reader = new TotalSecondsReader(); IReader<DateTime, bool> projection = reader.DiMap((DateTime dt) => dt.TimeOfDay, d => d % 2 == 0); Assert.True(projection.Run(new DateTime(3271, 12, 11, 2, 3, 4))); }

This example maps the Reader from `TimeSpan`

to `DateTime`

by mapping in the opposite direction: The lambda expression `(DateTime dt) => dt.TimeOfDay`

returns the time of day of a given `DateTime`

. This value is a `TimeSpan`

value representing the time passed since midnight on that date.

The example also checks whether or not a `double`

value is an even number.

When the resulting `projection`

is executed, the expected result is `true`

because the input date and time is first converted to a time of day (*02:03:04*) by the `contraSelector`

. Then `TotalSecondsReader`

converts that duration to the total number of seconds: *2 * 60 * 60 + 3 * 60 + 4 = 7,384*. Finally, *7,384* is an even number, so the output is `true`

.

### Raw functions #

As already explained in the previous Reader articles, the `IReader`

interface is mostly a teaching device. In a language where you can treat functions as values, you don't need the interface. In C#, for example, the standard function delegates suffice. You can implement `DiMap`

directly on `Func<A, B>`

:

public static Func<R1, A1> DiMap<R, R1, A, A1>( this Func<R, A> func, Func<R1, R> contraSelector, Func<A, A1> coSelector) { return func.Select(coSelector).ContraMap(contraSelector); }

As promised, I've here flipped the order of methods in the chain, so that the implementation first calls `Select`

and then `ContraMap`

. This is entirely arbitrary, and I only did it to demonstrate that the order doesn't matter.

Here's another usage example:

[Fact] public void AreaOfDate() { Func<Version, int> func = v => v.Major + v.Minor * v.Build; Func<DateTime, double> projection = func.DiMap( (DateTime dt) => new Version(dt.Year, dt.Month, dt.Day), i => i * i * Math.PI); Assert.Equal( expected: 16662407.390443427686297314140028, actual: projection(new DateTime(1991, 12, 26))); }

This example starts with a nonsensical function that calculates a number from a `Version`

value. Using `DiMap`

the example then transforms `func`

into a function that produces a `Version`

from a `DateTime`

and also calculates the area of a circle with the radius `i`

.

Clearly, this isn't a *useful* piece of code - it only demonstrates how `DiMap`

works.

### Identity law #

As stated in the profunctor introduction, I don't intend to make much of the profunctor laws, since they are only reiterations of the (covariant) functor and contravariant functor laws. Still, an example (not a proof) of the profunctor identity law may be in order:

[Fact] public void ProfunctorIdentityLaw() { Func<Guid, int> byteRange = g => g.ToByteArray().Max() - g.ToByteArray().Min(); T id<T>(T x) => x; Func<Guid, int> projected = byteRange.DiMap<Guid, Guid, int, int>(id, id); var guid = Guid.NewGuid(); Assert.Equal(byteRange(guid), projected(guid)); }

This example uses another silly function. Given any `Guid`

, the `byteRange`

function calculates the difference between the largest and smallest byte in the value. Projecting this function with the identity function `id`

along both axes should yield a function with the same behaviour. The assertion phase generates an arbitrary `Guid`

and verifies that both `byteRange`

and `projected`

produce the same resulting value.

### Haskell #

As usual, I've adopted many of the concepts and ideas from Haskell. The notion of a profunctor is so exotic that, unlike the Contravariant type class, it's not (to my knowledge) part of the *base* library. Not that I've ever felt the need to import it, but if I did, I would probably use Data.Profunctor. This module defines a `Profunctor`

type class, of which a normal function `(->)`

is an instance. The type class defines the `dimap`

function.

We can replicate the above `AreaOfDate`

example using the `Profunctor`

type class, and the types and functions in the time library.

First, I'll implement `func`

like this:

func :: Num a => (a, a, a) -> a func (maj, min, bld) = maj + min * bld

Instead of using a `Version`

type (which I'm not sure exists in the 'standard' Haskell libraries) this function just uses a triple (three-tuple).

The projection is a bit more involved:

projection :: Day -> Double projection = dimap ((\(maj, min, bld) -> (fromInteger maj, min, bld)) . toGregorian) (\i -> toEnum (i * i) * pi) func

It basically does the same as the `AreaOfDate`

, but the lambda expressions look more scary because of all the brackets and conversions. Haskell isn't always more succinct than C#.

> projection $ fromGregorian 1991 12 26 1.6662407390443427e7

Notice that GHCi returns the result in scientific notation, so while the decimal separator seems to be oddly placed, the result is the same as in the C# example.

### Conclusion #

The Reader functor is not only a (covariant) functor and a contravariant functor. Since it's both, it's also a profunctor. And so what?

This knowledge doesn't seem immediately applicable, but shines an interesting light on the fabric of code. If you squint hard enough, most programming constructs look like functions, and functions are profunctors. I don't intent to go so far as to claim that 'everything is a profunctor', but the Reader profunctor is ubiquitous.

I'll return to this insight in a future article.

**Next:** Invariant functors.