2013-04-01 Agda by Example: Sorting

Agda?

The Haskell programmer is used to the pleasure of collaborating with a nice type system. Haskell itself hits quite a sweet spot of expressivity and manageability—type inference is (mostly) decidable, no subtyping, etc. However, in the past 30 years, new systems have been emerging that allow the user to encode many more properties at the type level. In fact, these systems are so expressive that they are often used as logical frameworks for mathematics rather than to write programs, and are thus often called “theorem provers” rather than “programming languages.” Thanks to a notorious correspondence, we know that these two activities are really the same.

Agda is one of the most prominent systems of this kind at the moment. Many Haskell programmers like it because it has very functional slant, doing everything without recurring to mechanised transformation of terms (of which Coq is the most famous offender). In this blog I will try to give some examples that will hopefully make you interested. This first post explains how sorting algorithms can be proven correct. The implementation is largely inspired by a presentation by Conor McBride.

This blog post was generated from a literate Agda file, that you can find here. I’m not going to explain how to install Agda here, you can refer to the wiki or the wonderful freenode channel. While I go over all concepts presented I won’t go in depth to keep things reasonably brief: this is intended to get a taste of what Agda is capable of rather than explaining all its features. If you want to read a document meant to be a more comprehensive introduction, you can refer to the many tutorials available—personally I recommend Ulf Norell’s Dependently Typed Programming in Agda.

Let’s get started!

A few types

Good old List s

After the module declaration, let’s warm up by defining a data type dear to functional programmers:

The syntax to declare this type resembles the syntax for GADTs in Haskell. Here X is the parametrised type—what Agda calls Set is more or less what Haskell calls * , the type of types, or “kind” in Haskell parlance. List is a type constructor which takes a type and “returns” a new type, List : Set → Set , much like Haskell’s [] :: * → * .

Then we have two constructors, [] for an empty list and _∷_ to cons an element to an existing list. Agda gives us great flexibility in the syntax: arbitrary operators can defined where _ indicates an argument, and identifiers are not limited to the usual mix of alphanumeric characters plus a few of symbols. In this case _∷_ is a binary operator. The fixity declaration is similar to what we would find in Haskell.

Let’s define foldr :

Nothing surprising here, apart from the fact that in the type signature we have to take the trouble of bringing the type variables into scope manually. In Agda, parameters in curly braces are implicit and the type checker will try to infer them by unification. This procedure can fail—term inference is predictably undecidable—in which case the implicits can be explicitly provided, also with curly braces. Here we are also omitting the type of the parameter A by using ∀ . We can do this since A appears as an argument to List later in the signature. For the converse reason we must provide a type for B .

Sums

Now another “boring” type, Either , plus the associated destructor ( either in Haskell):

Unhabited types

Now for a type with no inhabitants—no constructors:

What is Empty useful for? Well, if the user provides a term of type Empty , we can give back anything he might want, corresponding to the logical ex falso quodlibet:

The () is what is called an empty pattern: Agda knows that no closed term will be of type Empty , and thus lets us leave out the body of functions with arguments of that type. Note that in Haskell we can easily get terms of any type in various ways, the most straightforward being general recursion:

undefined :: forall a. a undefined = undefined

Agda makes sure that this is not possible, thus keeping the system consistent. This has very pleasant consequences, the most prominent being that all programs terminate. For this reason consistent systems must be Turing-incomplete (we can’t write an infinite loops!), and thus Agda lets us step out of these checks if we want to, although it is rarely needed—most algorithms we write are quite easily provably terminating. Note that consistency wasn’t put in Agda only to please mathematicians: given the expressivity of the type system type checking and evaluation are tightly intertwined, and thus we can send the compiler in an infinite loop if we can write one.

We use Empty to define something close to negation in logic:

For example we would expect terms of type ¬ (3 > 4) to exist. Here it starts being clear that types are very first class in Agda; functions can work on them as they do with ordinary values: in this case ¬ takes a type and forms another one.

Different Rel ations

We need one last ingredient before we can start sorting. We could write our sort for some specific data type, say integers, but why would we do that considering that we have a language that lets us express abstract structures very naturally?

Instead, we can give a general definition for a binary relation on a type X :

The Set₁ indicates that a relation between two Set s is “larger” than a Set itself—this is nothing to worry about now, but it follows a tradition in set theory that goes back to Russell to avoid paradoxes. Set is in fact a shorthand for Set₀ and represents the type of types of values: Empty : Set₀ : Set₁ : Set₂ : ... .

Then we define the type of decidable relations—we would expect relations like “less than” on natural numbers or “sortedness” on lists to be decidable:

That is, a decidable relation has a function that tells us if any x and y are related or not.

Now the interesting part. To sort a list, we need two relations on the elements of the list: some notion of equality and some ordering on the elements (in fact the latter requires the former). More formally, the equality will be an equivalence relation. To express abstract properties over types we can use a record, much like type classes are used in Haskell—only more flexible but without the big advantage of having automatic instance resolution:

The definition on Wikipedia translates literally to Agda. Same story for our ordering, which will need to be total:

Sorting

We can finally begin to sort. To do this we will define a module parametrised over a type and an ordering. Agda has a very flexible module system—in fact we have already been using it by defining records, which are implicitly modules.

We require the ordering relation to be decidable, and we bring in scope some fields of the records using open , so that we can use them directly.

Insertion sort

We want to represent bounded lists, but we also want the bounds to be possibly open. For this purpose we lift our type X in a data type that contains a top and bottom elements, that are respectively greater or equal and lower or equal than all the other elements.

For ⊥X⊤ to be useful, we lift our ordering to work with it, following our considerations about the top and bottom elements:

Note that this data type is different from what we have defined before: the parameters to the type constructor can vary between data constructors—much like in GADTs in Haskell. In Agda, “changing” (more formally “non linear”) parameters are known as indices, as opposed to non-changing parameters. Parameters are named and to the left of the colon, while the type to the right of the colon will determine the number and type of indices—in this case two ⊥X⊤ s (remember, Rel ⊥X⊤ = ⊥X⊤ → ⊥X⊤ → Set ). This kind of data type is known as inductive family.

We can now define the type of bounded, ordered lists:

nil will work with any bounds, provided that the lower l is less or equal than the upper u . cons will cons an element x to a list with x as a lower bound, and return a list with lower bound l , provided that l ≤̂ ⟦ x ⟧ . It’s clear from how cons work that the elements in OList will be ordered according to the ≤ relation.

We can easily get a plain list back from an OList :

With the right data types, writing and proving correct an insertion sort is a breeze—I encourage you to try and writing yourself checking the goals as you pattern match, the only non-trivial case being the last one:

Insertion sort is just a fold, where we use the type OList ⊥ ⊤ to represent a sorted list with open bounds:

Tree sort

Now for something more efficient, a tree sort. Firstly we’ll define a bounded, ordered binary tree:

The technique is similar to that employed in OList . Then we need a procedure to insert an element in an existing tree:

Again, the only tricky bit is the last one, where we need to convince Agda that y ≤ x given that ¬ (x ≤ y) .

Similar to isort′ , turning a List into a Tree is a simple fold:

Now we can define OList concatenation, with the twist of inserting a new element in the middle; and finally flatten :

Then we are good with yet another fold.

Propositional equality

Now lets put our module to work. We will need a type equipped with the appropriate relations: in this post I am going to use natural numbers.

For what concerns equality, we can actually define an inductive family that relates equal terms:

It’s worth mentioning what equal means here. I have mentioned earlier that “evaluation and typechecking are intertwined”: when the type checker has to decide if two types, or more generally two terms, are “the same”, it simply reduces them as far as possible (to their normal form) and then compares them syntactically, plus some additional laws. Remember, every Agda term is terminating, so this procedure itself is guaranteed to terminate. Thus, refl : ((λ x → x) 1) ≡ 1 is acceptable, and so on.

This notion of equality is often called definitional equality, as opposed to the user-level equality expressed by the inductive family we have just defined, which takes the name of propositional equality. Note that having a prop. equality in scope does not imply definitional equality for the related terms, unless the prop. equality is a closed term. In the general case we might have prop. equalities in scope that do not necessarily hold or involve abstracted variables, think of λ (p : 3 ≡ 1) → ... .

Let’s prove that ≡ is an equivalence relation, and a cong ruence law which will be useful later:

Here we use pattern matching in a new way: since the value of the indices of ≡ depends on the constructors, matching on a constructor refines the context with the new information. For example in sym pattern matching refl will unify y and x , turning them into the same variable in the context for the body of sym , and thus letting us invoke refl again. Pattern matching is a much more powerful notion in Agda that is in in most (even dependently typed) programming languages—it can not only change the context, but it will also constraint the possible constructors of other parameters, if they are of a type with indices and those indices have been refined. This collection of techniques is known as dependent pattern matching.

Natural numbers

The definition for naturals is the usual one—the pragmas are there so that we can use number literals.

Now for our ordering relation. Every number is greater or equal than zero, and if x ≤ y then x + 1 ≤ y + 1 :

With the help of the dual of s≤s , we can write our decision function for ≤ :

And the required laws to make a total order out of ≤ :

Finally, we can import the sorting functions. We’re done!

We can test our function:

A tap on C-c C-n willIBeSorted? will give the expected result:

0 ∷ 3 ∷ 4 ∷ 5 ∷ 7 ∷ 12 ∷ 40 ∷ []

This is my first decently-sized blog post, so please complain on Reddit!