There was an error submitting the form

Copied to clipboard

"This could be the start of a beautiful proof."

Constructors are red.

Types are blue.

Your code always works

Because Idris loves you.

In How Functional Programming Mattered, Zhenjiang Hu, John Hughes, and Meng Wang argue that (pure) functional programming has changed the way we think about programs. An important part of the reason why is equational reasoning and referential transparency.

As an example, Hu, Hughes, and Wang use a function to reverse a list to show how functional languages such as Haskell allow us to write properties of programs as programs, and to prove such properties by hand, just like in mathematics. But functional languages such as Agda and Idris go a step further. They have dependent types, which means we can write proofs of properties of programs as programs.

In this blog post, we'll use Haskell (GHC 7.10.2 and QuickCheck 2.8.1) to test that a function to reverse a list satisfies the property that such a function is its own inverse, and Idris (0.9.19) to prove that it does.

Let's begin by creating a file `reverse-reverse.hs`

for the Haskell
code. We're not going to use the standard `++`

and `reverse`

functions, so let's hide them:

```
import Prelude hiding ((++), reverse)
```

We can now add the definition of the `++`

function to append lists.
This is not necessary because it corresponds to the standard `++`

function, but it can be useful to include it for reference:

```
infixr 5 ++
(++) :: [a] -> [a] -> [a]
[] ++ ys = ys
(x:xs) ++ ys = x : xs ++ ys
```

Now, let's define a `reverse`

function to reverse lists:

```
reverse :: [a] -> [a]
reverse [] = []
reverse (x:xs) = reverse xs ++ [x]
```

Given a definition like the one above, we can express as a function
the property that `reverse`

should be its own inverse:

```
reverseReverse :: [Integer] -> Bool
reverseReverse xs = reverse (reverse xs) == xs
```

This function returns `True`

if applying `reverse`

twice to a list
yields the same list, and `False`

otherwise:

```
ghci> reverseReverse [1,2,3,4,5]
True
```

We can test this property using QuickCheck, which will generate 100 lists of integers and check that the property holds for all of them:

```
ghci> import Test.QuickCheck
ghci> quickCheck reverseReverse
+++ OK, passed 100 tests.
```

We've just tested that our definition of `reverse`

satisfies the
`reverseReverse`

property. Equational reasoning, however, gives us the
power to reason about definitions. Thus, we can actually prove that
the property holds for all finite lists. We can do it by induction on
the given list, as follows:

- In the case of an empty list:

`reverse (reverse [])`

`==`

(by definition of `reverse`

)

`reverse []`

`==`

(by definition of `reverse`

)

`[]`

- And, in the case of a list
`x:xs`

:

`reverse (reverse (x:xs))`

`==`

(by definition of `reverse`

)

`reverse (reverse xs ++ [x])`

`==`

(see below)

`reverse [x] ++ reverse (reverse xs)`

`==`

(by inductive hypothesis)

`reverse [x] ++ xs`

`==`

(by definition of `reverse`

)

`(reverse [] ++ [x]) ++ xs`

`==`

(by definitions of `reverse`

and `++`

)

`x:xs`

In order to prove that `reverse`

is its own inverse, we need to prove
that `reverse`

and `++`

commute for all finite lists:

```
reverse (xs ++ ys) == reverse ys ++ reverse xs
```

We won't prove this property here. Instead, we'll assume that it holds (and it does, we could prove it by induction too).

The above proof is one of the most basic examples of correctness proofs, but we did it by hand. Functional languages with dependent types make it possible to write such proofs as programs, which is even more impressive.

Let's create a file `reverse-reverse.idr`

for the Idris code. We're
not going to use the standard `reverse`

function, so let's hide it:

```
%hide reverse
```

Next, we can write the type of the `reverse`

function:

```
reverse : List a -> List a
```

Functional languages like Idris are great for type-driven and incremental development, and the Idris mode for editors such as Emacs and Vim take full advantage of that.

If we place the cursor on top of `reverse`

(the name of the function),
we can make a new definition with `C-c C-s`

for Emacs (or `\d`

for
Vim):

```
reverse : List a -> List a
reverse xs = ?reverse_rhs
```

This adds a line for the definition of `reverse`

and a hole
(`?reverse_rsh`

) to fill with the actual definition. We can place the
cursor on `xs`

and split cases with `C-c C-c`

(or `\c`

):

```
reverse : List a -> List a
reverse [] = ?reverse_rhs_1
reverse (x :: xs) = ?reverse_rhs_2
```

We now have two equations for the definition of `reverse`

and two
holes to fill with appropriate definitions. After loading the file
with `C-c C-l`

(or `\r`

), we can place the cursor on the
`?reverse_rhs_1`

hole and ask for its type with `C-c C-t`

(or `\t`

):

```
a : Type
--------------------------------------
Main.reverse_rhs_1 : List a
```

The only thing we know is that `a`

is a `Type`

and we need to provide
a value of type `List a`

. The only option we have is to fill the hole
with an empty list:

```
reverse : List a -> List a
reverse [] = []
reverse (x :: xs) = ?reverse_rhs_2
```

We can now put the cursor over the `?reverse_rhs_2`

hole and ask for
its type:

```
a : Type
x : a
xs : List a
--------------------------------------
Main.reverse_rhs_2 : List a
```

In this case, we have that `a`

is a `Type`

, `x`

is a value of type
`a`

, `xs`

is a list of elements of type `a`

, and we need to provide a
value of type `List a`

. There's more than one way to build such a
list, but we already know the answer for reversing a list:

```
reverse : List a -> List a
reverse [] = []
reverse (x :: xs) = reverse xs ++ [x]
```

Since we have dependent types, we can define a new function for the
proof that `reverse`

is its own inverse:

```
reverseReverse : (xs : List a) -> reverse (reverse xs) = xs
```

Given a value `xs`

of type `List a`

, `reverseReverse xs`

is a proof of
the property that `reverse (reverse xs) = xs`

. This is similar to what
we did with Haskell, but at the type level -- the `=`

here is an
actual data type representing equality.

Let's make a new definition for `reverseReverse`

:

```
reverseReverse : (xs : List a) -> reverse (reverse xs) = xs
reverseReverse xs = ?reverseReverse_rhs
```

And split cases for `xs`

:

```
reverseReverse : (xs : List a) -> reverse (reverse xs) = xs
reverseReverse [] = ?reverseReverse_rhs_1
reverseReverse (x :: xs) = ?reverseReverse_rhs_2
```

In the case of an empty list, this is the type of the hole:

```
a : Type
--------------------------------------
Main.reverseReverse_rhs_1 : [] = []
```

We can prove that `[] = []`

by reflexivity. Actually, we can place the
cursor on the hole and tell Idris to search for a proof with `C-c C-a`

(or `\o`

). Idris will fill the hole with a `Refl`

, which is the only
constructor for the `=`

type:

```
reverseReverse : (xs : List a) -> reverse (reverse xs) = xs
reverseReverse [] = Refl
reverseReverse (x :: xs) = ?reverseReverse_rhs_2
```

Let's check the type of the second hole:

```
a : Type
x : xs
xs : List a
--------------------------------------
Main.reverseReverse_rhs_2 : reverse (reverse xs ++ [x]) = x :: xs
```

We know that we need a proof of the property that `reverse`

and `++`

commute. This time, we can't simply assume that it holds -- we have to
either prove it or postulate it if we want to use it. We should prove
it, but let's postulate it and focus on `reverseReverse`

:

```
postulate
reverseAppend : (xs : List a) -> (ys : List a) ->
reverse (xs ++ ys) = reverse ys ++ reverse xs
```

We can `rewrite`

the left-hand side of the goal using the
`reverseAppend`

property for `reverse xs`

and `[x]`

:

```
reverseReverse : (xs : List a) -> reverse (reverse xs) = xs
reverseReverse [] = Refl
reverseReverse (x :: xs) =
rewrite reverseAppend (reverse xs) [x] in ?reverseReverse_rhs_2
```

Let's ask for the type of the new hole:

```
a : Type
x : xs
xs : List a
_rewrite_rule : x :: reverse (reverse xs) = reverse (reverse xs ++ [x])
--------------------------------------
Main.reverseReverse_rhs_2 : x :: reverse (reverse xs) = x :: xs
```

The type now includes a rewrite rule that explicitly tells us what was
replaced in the previous step. The left-hand side of the new goal can
be rewritten using the inductive hypothesis (that is, `reverseReverse xs`

):

```
reverseReverse : (xs : List a) -> reverse (reverse xs) = xs
reverseReverse [] = Refl
reverseReverse (x :: xs) =
rewrite reverseAppend (reverse xs) [x] in
rewrite reverseReverse xs in ?reverseReverse_rhs_2
```

Let's ask for the type of the new hole:

```
a : Type
x : xs
xs : List a
_rewrite_rule : x :: reverse (reverse xs) = reverse (reverse xs ++ [x])
_rewrite_rule1 : xs = reverse (reverse xs)
--------------------------------------
Main.reverseReverse_rhs_2 : x :: xs = x :: xs
```

The only thing remaining is to prove that `x :: xs = x :: xs`

, which
is true by reflexivity:

```
reverseReverse : (xs : List a) -> reverse (reverse xs) = xs
reverseReverse [] = Refl
reverseReverse (x :: xs) =
rewrite reverseAppend (reverse xs) [x] in
rewrite reverseReverse xs in Refl
```

"It type checks! Ship it!"

Many thanks to Eric Jones for the idea for this blog post.

For more information about Idris and complete tutorials, see Idris and the Idris documentation.

All the quotes in this blog post were taken from the Idris mode for Emacs.

Published on Sep. 28, 2015

Join our community of avid readers and stay informed with the latest articles, tips, and insights delivered straight to your inbox. Don't miss out on valuable content – subscribe now and be part of the conversation!

We care about your data. Check out our Privacy Policy.