Functors that are both co- and contravariant. An article for C# programmers.

This article series is part of a larger series of articles about functors, applicatives, and other mappable containers. Particularly, you've seen examples of both functors and contravariant functors.

What happens if you, so to speak, combine those two?

### Mapping in both directions #

A profunctor is like a bifunctor, except that it's contravariant in one of its arguments (and covariant in the other). Usually, you'd list the contravariant argument first, and the covariant argument second. By that convention, a hypothetical `Profunctor<A, B>` would be contravariant in `A` and covariant in `B`.

In order to support such mapping, you could give the class a `DiMap` method:

```public sealed class Profunctor<A, B>
{
public Profunctor<A1, B1> DiMap<A1, B1>(Func<A1, A> contraSelector, Func<B, B1> coSelector)

// ...```

Contrary to (covariant) functors, where C# will light up some extra compiler features if you name the mapping method `Select`, there's no extra language support for profunctors. Thus, you can call the method whatever you like, but here I've chosen the name `DiMap` just because that's what the Haskell Profunctors package calls the corresponding function.

Notice that in order to map the contravariant type argument `A` to `A1`, you must supply a selector that moves in the contrary direction: from `A1` to `A`. Mapping the covariant type argument `B` to `B1`, on the other hand, goes in the same direction: from `B` to `B1`.

An example might look like this:

```Profunctor<TimeSpan, double> profunctor = CreateAProfunctor();
Profunctor<string, bool> projection =
profunctor.DiMap<string, bool>(TimeSpan.Parse, d => d % 1 == 0);```

This example starts with a profunctor where the contravariant type is `TimeSpan` and the covariant type is `double`. Using `DiMap` you can map it to a `Profunctor<string, bool>`. In order to map the `profunctor` value's `TimeSpan` to the `projection` value's `string`, the method call supplies `TimeSpan.Parse`: a (partial) function that maps `string` to `TimeSpan`.

The second argument maps the `profunctor` value's `double` to the `projection` value's `bool` by checking if `d` is an integer. The lambda expression `d => d % 1 == 0` implements a function from `double` to `bool`. That is, the profunctor covaries with that function.

### Covariant mapping #

Given `DiMap` you can implement the standard `Select` method for functors.

```public Profunctor<A, B1> Select<B1>(Func<B, B1> selector)
{
return DiMap<A, B1>(x => x, selector);
}```

Equivalently to bifunctors, when you have a function that maps both dimensions, you can map one dimension by using the identity function for the dimension you don't need to map. Here I've used the lambda expression `x => x` as the identity function.

You can use this `Select` method with standard method-call syntax:

```Profunctor<DateTime, string> profunctor = CreateAProfunctor();
Profunctor<DateTime, int> projection = profunctor.Select(s => s.Length);```

or with query syntax:

```Profunctor<DateTime, TimeSpan> profunctor = CreateAProfunctor();
Profunctor<DateTime, int> projection = from ts in profunctor
select ts.Minutes;```

All profunctors are also covariant functors.

### Contravariant mapping #

Likewise, given `DiMap` you can implement a `ContraMap` method:

```public Profunctor<A1, B> ContraMap<A1>(Func<A1, A> selector)
{
return DiMap<A1, B>(selector, x => x);
}```

Using `ContraMap` is only possible with normal method-call syntax, since C# has no special understanding of contravariant functors:

```Profunctor<long, DateTime> profunctor = CreateAProfunctor();
Profunctor<string, DateTime> projection = profunctor.ContraMap<string>(long.Parse);```

All profunctors are also contravariant functors.

While you can implement `Select` and `ContraMap` from `DiMap`, it's also possible to go the other way. If you have `Select` and `ContraMap` you can implement `DiMap`.

### Laws #

In the overall article series, I've focused on the laws that govern various universal abstractions. In this article, I'm going to treat this topic lightly, since it'd mostly be a reiteration of the laws that govern co- and contravariant functors.

The only law I'll highlight is the profunctor identity law, which intuitively is a generalisation of the identity laws for co- and contravariant functors. If you map a profunctor in both dimensions, but use the identity function in both directions, nothing should change:

```Profunctor<Guid, string> profunctor = CreateAProfunctor();
Profunctor<Guid, string> projection = profunctor.DiMap((Guid g) => g, s => s);```

Here I've used two lambda expressions to implement the identity function. While they're two different lambda expressions, they both 'implement' the general identity function. If you aren't convinced, we can demonstrate the idea like this instead:

```T id<T>(T x) => x;
Profunctor<Guid, string> profunctor = CreateAProfunctor();
Profunctor<Guid, string> projection = profunctor.DiMap<Guid, string>(id, id);```

This alternative representation defines a local function called `id`. Since it's generic, you can use it as both arguments to `DiMap`.

The point of the identity law is that in both cases, `projection` should be equal to `profunctor`.

### Usefulness #

Are profunctors useful in everyday programming? So far, I've found no particular use for them. This mirrors my experience with contravariant functors, which I also find little use for. Why should we care, then?

It turns out that, while we rarely work explicitly with profunctors, they're everywhere. Normal functions are profunctors.

In addition to normal functions (which are both covariant and contravariant) other profunctors exist. At the time that I'm writing this article, I've no particular plans to add articles about any other profunctors, but if I do, I'll add them to the above list. Examples include Kleisli arrows and profunctor optics.

The reason I find it worthwhile to learn about profunctors is that this way of looking at well-behaved functions shines an interesting light on the fabric of computation, so to speak. In a future article, I'll expand on that topic.

### Conclusion #

A profunctor is a functor that is covariant in one dimension and contravariant in another dimension. While various exotic examples exist, the only example that you'd tend to encounter in mainstream programming is the Reader profunctor, also known simply as functions.

Next: Reader as a profunctor.

Are profunctors useful in everyday programming? So far, I've found no particular use for them.

I have not found the concept of a profunctor useful in everyday programming, but I have found one very big use for them.

I am the maintainer of Elmish.WPF. This project makes it easy to create a WPF application in the style of the Model-View-Update / MVU / Elm Architecture. In a traditional WPF application, data goes between a view model and WPF via properties on that view model. Elmish.WPF makes that relationship a first-class concept via the type `Binding<'model, 'msg>`. This type is a profunctor; contravariant in `'model` and covariant in `'msg`. The individual mapping functions are `Binding.mapModel` and `Binding.mapMsg` respectively.

This type was not always a profunctor. Recall that a profunctor is not really just a type but a type along with its mapping functions (or a single combined function). In versions 1, 2, and 3 of Elmish.WPF, this type is not a profunctor due to the missing mapping function(s). I added the mapping functions in version 4 (currently a prelease) that makes `Binding<'model, 'msg>` (along with those functions) a profunctor. This abstraction has made it possible to significantly improve both the internal implementation as well as the public API.

As you stated, a single function `a -> b` is a profunctor; contravariant in `a` and covariant in `b`. I think of this as the canonical profunctor, or maybe "the original" profunctor. I think it is interesting to compare each profunctor to this one. In the case of `Binding<'model, 'msg>`, is implemented by two functions: one from the (view) model to WPF with type `'model -> obj` that we could call `toWpf` and one from WPF with type `obj -> 'msg` that we could call `fromWpf`. Of course, composing these two functions results in a single function that is a profunctor in exactly the way we expect.

Now here is something that I don't understand. In addition to `Binding.mapModel` and `Binding.mapMsg`, I have discovered another function with useful behavior in the function `Binding.mapMsgWithModel`. Recall that a function `a -> b` is not just profunctor in (contravariant) `a` and (covariant) `b`, but it is also a monad in `b` (with `a` fixed). The composed function `toWpf >> fromWpf` is such a monad and `Binding.mapMsgWithModel` is its "`bind`" function. The leads one to think that `Binding<'model, 'msg>` could be a monad in `'msg` (with `'model` fixed). My intuition is that this is not the case, but maybe I am wrong.

2021-11-02 01:38 UTC

Tyson, it's great to learn that there are other examples of useful profunctors in the wild. It might be useful to add it to a 'catalogue' of profunctor examples, if someone ever compiles such a list - but perhaps it's a little to specific for a general-purpose catalogue...

After much digging around in the source code you linked, I managed to locate the definition of Binding<'model, 'msg>, which, however, turns out to be too complex for me to do a full analysis on a Saturday morning.

One of the cases, however, the TwoWayData<'model, 'msg> type looks much like a lens - another profunctor example.

Might `Binding<'model, 'msg>` also form a monad?

One simple test I've found useful for answering such a question is to consider whether a lawful `join` function exists. In Haskell, the `join` function has the type `Monad m => m (m a) -> m a`. The intuitive interpretation is that if you can 'flatten' a nested functor, then that functor is also a monad.

So the question is: Can you reasonably write a function with the type `Binding<'model, Binding<'model, 'msg>> -> Binding<'model, 'msg>`?

If you can write a lawful implementation of this `join` function, the `Binding<'model, 'msg>` forms a monad.

2021-11-06 10:56 UTC

Thank you for linking to the deinition of `Binding<'model, 'msg>`. I should have done that. Yes, the `TwoWayData<'model, 'msg>` case is probably the simplest. Good job finding it. I have thought about implementing such a `join` function, but it doesn't seem possible to me.

2021-11-15 14:13 UTC

### Wish to comment?

You can add a comment to this post by sending me a pull request. Alternatively, you can discuss this post on Twitter or somewhere else with a permalink. Ping me with the link, and I may respond.

#### Published

Monday, 01 November 2021 06:59:00 UTC

#### Tags

"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Monday, 01 November 2021 06:59:00 UTC