2
data Nat = Zero | Succ Nat
type Predicate = (Nat -> Bool)

-- forAllNat p = (p n) for every finite defined n :: Nat

implies :: Bool -> Bool -> Bool
implies p q = (not p) || q 

basecase :: Predicate -> Bool
basecase p = p Zero 

jump :: Predicate -> Predicate
jump p n = implies (p n) (p (Succ n)) 

indstep :: Predicate -> Bool
indstep p = forallnat (jump p) 

Question:

Prove that if basecase p and indstep p, then forAllNat p

What I do not understand is that if basecase p and indstep p, so forAllNat p should be True, of course.

I think basecase p says that P(0) is true, and indstep p says that P(Succ n) which is P(n+1) is true And we need to prove P(n) is true. Am I right? Any suggestion about how to do this?

Cactus
  • 27,075
  • 9
  • 69
  • 149
Joe
  • 107
  • 1
  • 10

2 Answers2

8

As Benjamin Hodgson indicates, you can't quite prove that in Haskell. However, you can prove a statement with slightly stronger preconditions. I'll also ignore the unnecessary complexity of Bool.

{-# LANGUAGE GADTs, KindSignatures, DataKinds, RankNTypes, ScopedTypeVariables #-}

data Nat = Z | S Nat

data Natty :: Nat -> * where
  Zy :: Natty 'Z
  Sy :: Natty n -> Natty ('S n)

type Base (p :: Nat -> *) = p 'Z
type Step (p :: Nat -> *) = forall (n :: Nat) . p n -> p ('S n)

induction :: forall (p :: Nat -> *) (n :: Nat) .
             Base p -> Step p -> Natty n -> p n
induction b _ Zy = b
induction b s (Sy n) = s (induction b s n)
dfeuer
  • 48,079
  • 5
  • 63
  • 167
  • 2
    Very cool. I haven't seen singletons used for theorem proving like this before. Of course, Haskell's usual `_|_`-related shortcomings mean you can "prove" the induction principle for ill-founded `p`s or `n`s which would be ruled out by a termination checker in a real proof assistant. – Benjamin Hodgson Apr 07 '16 at 17:51
  • @BenjaminHodgson, indeed, the lack of termination checking is annoying sometimes. It's interesting sometimes to figure out exactly which arguments must be singletons and which can just be proxies. It really focuses your attention on where calculation is happening. – dfeuer Apr 07 '16 at 17:55
7

You can't prove this within Haskell. (Turns out you can.) The language is not dependently typed enough. It's a programming language, not a proof assistant. I think the assignment probably expects you to prove it on pencil and paper.

You can do it in Agda though.

data Nat : Set where
  zero : Nat
  suc : Nat -> Nat

Pred : Set -> Set1
Pred A = A -> Set

Universal : {A : Set} -> Pred A -> Set
Universal {A} P = (x : A) -> P x

Base : Pred Nat -> Set
Base P = P zero
Step : Pred Nat -> Set
Step P = (n : Nat) -> P n -> P (suc n)

induction-principle : (P : Pred Nat) -> Base P -> Step P -> Universal P
induction-principle P b s zero = b
induction-principle P b s (suc n) = s n (induction-principle P b s n)

(You may recognise induction-principle as being Nat's foldr.)

You may be able to get something a bit like this when TypeInType lands in GHC 8. It won't be pretty though.

Community
  • 1
  • 1
Benjamin Hodgson
  • 42,952
  • 15
  • 108
  • 157
  • 4
    I don't think `TypeInType` has any impact. The trouble is not types vs. kinds but terms vs. types. There's no way to prove in Haskell that everything of kind `Nat` can be constructed from `'Z` and `'S`. In fact, it's not even *true*, because the `Nat` kind is also inhabited by "stuck types" like `Any`. – dfeuer Apr 07 '16 at 17:33