Another most likely useless set of invariant functors that nonetheless exist.

It turns out that all contravariant functors are also invariant functors.

Is this useful? Let me, like in the previous article, be honest and say that if it is, I'm not aware of it. Thus, if you're interested in practical applications, you can stop reading here. This article contains nothing of practical use - as far as I can tell.

### Because it's there #

Why describe something of no practical use?

Why do some people climb Mount Everest? Because it's there, or for other irrational reasons. Which is fine. I've no personal goals that involve climbing mountains, but I happily engage in other irrational and subjective activities.

One of them, apparently, is to write articles of software constructs of no practical use, because it's there.

All contravariant functors are also invariant functors, even if that's of no practical use. That's just the way it is. This article explains how, and shows a few (useless) examples.

I'll start with a few Haskell examples and then move on to showing the equivalent examples in C#. If you're unfamiliar with Haskell, you can skip that section.

For Haskell you can find an existing definition and implementations in the invariant package. It already makes most 'common' contravariant functors `Invariant` instances, including `Predicate`, `Comparison`, and `Equivalence`. Here's an example of using `invmap` with a predicate.

First, we need a predicate. Consider a function that evaluates whether a number is divisible by three:

```isDivisbleBy3 :: Integral a => a -> Bool
isDivisbleBy3 = (0 ==) . (`mod` 3)```

While this is already conceptually a contravariant functor, in order to make it an `Invariant` instance, we have to enclose it in the `Predicate` wrapper:

```ghci> :t Predicate isDivisbleBy3
Predicate isDivisbleBy3 :: Integral a => Predicate a```

This is a predicate of some kind of integer. What if we wanted to know if a given duration represented a number of picoseconds divisible by three? Silly example, I know, but in order to demonstrate invariant mapping, we need types that are isomorphic, and NominalDiffTime is isomorphic to a number of picoseconds via its `Enum` instance.

```p :: Enum a => Predicate a
p = invmap toEnum fromEnum \$ Predicate isDivisbleBy3```

In other words, it's possible to map the `Integral` predicate to an `Enum` predicate, and since `NominalDiffTime` is an `Enum` instance, you can now evaluate various durations:

```ghci> (getPredicate p) \$ secondsToNominalDiffTime 60
True
ghci> (getPredicate p) \$ secondsToNominalDiffTime 61
False```

This is, as I've already announced, hardly useful, but it's still possible. Unless you have an API that requires an `Invariant` instance, it's also redundant, because you could just have used `contramap` with the predicate:

```ghci> (getPredicate \$ contramap fromEnum \$ Predicate isDivisbleBy3) \$ secondsToNominalDiffTime 60
True
ghci> (getPredicate \$ contramap fromEnum \$ Predicate isDivisbleBy3) \$ secondsToNominalDiffTime 61
False```

When mapping a contravariant functor, only the contravariant mapping argument is required. The `Invariant` instances for `Contravariant` simply ignores the covariant mapping argument.

### Specification as an invariant functor in C# #

My earlier article The Specification contravariant functor takes a more object-oriented view on predicates by examining the Specification pattern.

As outlined in the introduction, while it's possible to add a method called `InvMap`, it'd be more idiomatic to add a non-standard `Select` method:

```public static ISpecification<T1> Select<T, T1>(
this ISpecification<T> source,
Func<T, T1> tToT1,
Func<T1, T> t1ToT)
{
return source.ContraMap(t1ToT);
}```

This implementation ignores `tToT1` and delegates to the existing `ContraMap` method.

Here's a unit test that demonstrates an example equivalent to the above Haskell example:

```[Theory]
[InlineData(60,  true)]
[InlineData(61, false)]
public void InvariantMappingExample(long seconds, bool expected)
{
ISpecification<long> spec = new IsDivisibleBy3Specification();
ISpecification<TimeSpan> mappedSpec =
spec.Select(ticks => new TimeSpan(ticks), ts => ts.Ticks);
Assert.Equal(
expected,
mappedSpec.IsSatisfiedBy(TimeSpan.FromSeconds(seconds)));
}```

Again, while this is hardly useful, it's possible.

### Conclusion #

All contravariant functors are invariant functors. You simply use the 'normal' contravariant mapping function (`contramap` in Haskell). This enables you to add an invariant mapping (`invmap`) that only uses the contravariant argument (`b -> a`) and ignores the covariant argument (`a -> b`).

Invariant functors are, however, not particularly useful, so neither is this result. Still, it's there, so deserves a mention. Enough of that, though.

### 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, 06 February 2023 06:42:00 UTC

#### Tags

"Our team wholeheartedly endorses Mark. His expert service provides tremendous value."
Hire me!
Published: Monday, 06 February 2023 06:42:00 UTC