This post is about the strength function, Lenses, and strong functors. Specifically, it’s about how we can generalise strength using lenses to work on any product type, not just tuples.

If you like, you can skip straight to the good bit where we derive a generalised strength.

For our purposes, strength is a function which “everts” a tuple containing some functor, (a, f b), turning it inside out to result in a functor of a tuple f (a, b). We can write this function as follows:

Note that the choice of f b being the second item in the tuple is fairly arbitrary, and we could just as easily have put it as the first. Our choice is important to make code cleaner later in the article.

In Category Theory, a functor allowing this operation is said to be strong. A more formal definition of a strong functors can be found in Edward Kmett’s excellent article “Deriving strength from laziness”, which also notes that all Haskell Functors are strong.

So what?

This might seem a bit pointless, but turns out to be useful in a number of cases. For example, take MapReduce. a MapReduce program defines two functions, map, and reduce. Haskell types for these functions might look like this:

mrMap :: ka -> va -> [(kb, vb)]
mrReduce :: kb -> [vb] -> [output]

where ka denotes “key type a”, equivalent to in_key from the MapReduce slides, and “vb” denotes “value b”, equivalent to intermediate_value. Let’s suppose we want to write the “identity” reduce function. That is, a function which yields the output of the mrMap function unchanged.

What if it’s not a pair?

We’ve seen that our examples worked out particularly well for us, but only because we picked an implementation of strength matching our use-cases. How would we deal with nested Functors as the first item? For that matter, what about 3- and 4-tuples? In fact, what about records?

What we want is to be able to use strength on any product type, including records, with some easy way to specify which field is the functor we want to evert.

Lenses to the rescue

Fortunately, the lens package is here to help. This gives us a nice way of passing around getters and setters, and crucially lets us do polymorphic updates. That means that when modifying a field, we can also change its type (and therefore also the type of the ‘parent’ record). We’ve seen this already in the definition of parsePair, where the second item in a tuple was modified “in place”, while changing its type from Value to Parser v.

The essence of strength

Clearly, our new function won’t be able to use pattern matching. We’ll rewrite the original function without pattern matching first.

The good bit

From here it’s pretty clear how to proceed: we just need to replace the hard-coded lens with one passed in as an argument. Note that you’ll need the RankNTypes extension for this to work (i’m not sure why!)

Note we’ve replaced the lambda expression using ??, an infix version of flip which you can read as a placeholder for a missing argument.

Finally, to explain the type signature. The type Lens s t a b can be read as “A lens into a record of type s to a field of type ‘a’. The field can be modified to be of type ‘b’, resulting in a record of type ‘t’.”

What we’ve specified is a Lens s t (f a) a, which is a lens on a record s, containing a field of type f a. The “target” record is of type t, and has a field of type a- the type of elements of the functor. Our strong function now returns an f t, the wrapped record.