GHC 7.6.1 comes with new features for programming at the type level, including datatype promotion. Taking the example about type-level naturals and vectors from there, I’d like to be able to write functions on vectors that rely on basic laws of arithmetic.
Unfortunately, even though the laws I want are typically easy to prove on inductive naturals by case analysis and induction, I’m doubt I can convince the type-checker of this. As a simple example, type-checking the naive reverse function below requires a proof that n + Su Ze ~ Su n.
Is there any way I can supply that proof, or am I really in the realm of full-blown dependent types now?
{-# LANGUAGE DataKinds, KindSignatures, GADTs, TypeFamilies, TypeOperators #-}
data Nat = Ze | Su Nat
data Vec :: * -> Nat -> * where
Nil :: Vec a Ze
Cons :: a -> Vec a n -> Vec a (Su n)
type family (m :: Nat) + (n :: Nat) :: Nat
type instance Ze + n = n
type instance (Su m + n) = Su (m + n)
append :: Vec a m -> Vec a n -> Vec a (m + n)
append Nil ys = ys
append (Cons x xs) ys = Cons x (append xs ys)
rev :: Vec a n -> Vec a n
rev Nil = Nil
rev (Cons x xs) = rev xs `append` Cons x Nil
(Note: I have only type-checked (and not actually run) any of this code.)
Approach 1
Actually, you can manipulate proofs by storing them in GADTs. You’ll need to turn on
ScopedTypeVariablesfor this approach to work.Actually, perhaps interesting motivation for the
Prooftype above, I originally had justBut, this didn’t work: GHC rightly complained that just because we know
(Su n)+1 = Su (Su n)doesn’t imply that we known+1 = Su n, which is what we need to know to make the recursive call torevin theConscase. So I had to expand the meaning of aProofto include a proof of all equalities for naturals up to and includingn— essentially a similar thing to the strengthening process when moving from induction to strong induction.Approach 2
After a bit of reflection, I realized that it turns out the class is a bit superfluous; that makes this approach especially nice in that it doesn’t require any extra extensions (even
ScopedTypeVariables) and doesn’t introduce any extra constraints to the type ofVec.Approach 3
Alternately, if you switch the implementation of
reva bit to cons the last element onto the reversed initial segment of the list, then the code can look a bit more straightforward. (This approach also requires no additional extensions.)Approach 4
Just like approach 3, but again observing that the type classes are not necessary.