Sorting Slower with Style

Insertion sort can be
implemented in a similar way - this time by putting one recursion scheme inside
of another.

Read on for details.

Apomorphisms

These guys don’t seem to get a lot of love in the recursion scheme tutorial du
jour, probably because they might be the first scheme you encounter that looks
truly weird on first glance. But apo is really not bad at all - I’d go so
far as to call apomorphisms straightforward and practical.

So: if you remember your elementary recursion schemes, you can say that apo
is to ana as para is to cata. A paramorphism gives you access to a value
of the original input type at every point of the recursion; an apomorphism lets
you terminate using a value of the original input type at any point of the
recursion.

This is pretty useful - often when traversing some structure you just want to
bail out and return some value on the spot, rather than continuing on recursing
needlessly.

A good introduction is the toy ‘mapHead’ function that maps some other function
over the head of a list and leaves the rest of it unchanged. Let’s first rig
up a hand-rolled list type to illustrate it on:

So, inside any base functor on the right hand side we’re going to be dealing
with some ‘Either’ values. The ‘Left’ branch indicates that we’re going to
terminate the recursion using whatever value we pass, whereas the ‘Right’
branch means we’ll continue recursing as per normal.

In the case of ‘mapHead’, the coalgebra is saying:

deconstruct the list; if it has no elements just return an empty list

if the list has at least one element, return the list constructed by
prepending ‘f h’ to the existing tail.

Here the ‘Left’ branch is used to terminate the recursion and just return the
existing tail on the spot.

Example Two

That was pretty easy, so let’s take it up a notch and implement list
concatenation:

This one is slightly more involved, but the principles are almost entirely the
same. If both lists are empty we just return an empty list, and if the first
list has at most one element we return the list constructed by jamming the
second list onto it. The ‘Left’ branch again just terminates the recursion and
stops everything there.

If both lists are nonempty? Then we actually do some work and recurse, which
is what the ‘Right’ branch indicates.

So hopefully you can see there’s nothing too weird going on - the coalgebras
are really simple once you get used to the Either constructors floating around
in there.

Paramorphisms involve an algebra that gives you access to a value of the
original input type in a pair - a product of two values. Since apomorphisms
are their dual, it’s no surprise that you can give them a value of the original
input type using ‘Either’ - a sum of two values.

Insertion Sort

So yeah, my favourite example of an apomorphism is for implementing the ‘inner
loop’ of insertion sort, a famous worst-case comparison-based
sort. Granted that insertion sort itself is a bit of a toy algorithm, but the
pattern used to implement its internals is pretty interesting and more broadly
applicable.

This animation found on
Wikipedia
illustrates how insertion sort works:

We’ll actually be doing this thing in reverse - starting from the right-hand
side and scanning left - but here’s the inner loop that we’ll be concerned
with: if we’re looking at two elements that are out of sorted order, slide the
offending element to where it belongs by pushing it to the right until it hits
either a bigger element or the end of the list.

As an example, picture the following list:

[3, 1, 1, 2, 4, 3, 5, 1, 6, 2, 1]

The first two elements are out of sorted order, so we want to slide the 3
rightwards along the list until it bumps up against a larger element - here
that’s the 4.

The following function describes how to do that in general for our hand-rolled
list type:

Addendum: Using Regular Lists

You’ll note that the ‘fromList’ and ‘knockbackL’ functions above operate on
regular Haskell lists. The short of it is that it’s easy to do this;
recursion-schemes defines a data family called ‘Prim’ that basically endows
lists with base functor constructors of their own. You just need to use ‘Nil’
in place of ‘[]’ and ‘Cons’ in place of ‘(:)’.

Here’s insertion sort implemented in the same way, but for regular lists: