summaryrefslogtreecommitdiff
path: root/testsuite/tests/gadt/termination.hs
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/tests/gadt/termination.hs')
-rw-r--r--testsuite/tests/gadt/termination.hs183
1 files changed, 183 insertions, 0 deletions
diff --git a/testsuite/tests/gadt/termination.hs b/testsuite/tests/gadt/termination.hs
new file mode 100644
index 0000000000..be2431b812
--- /dev/null
+++ b/testsuite/tests/gadt/termination.hs
@@ -0,0 +1,183 @@
+{-# LANGUAGE GADTs, Rank2Types #-}
+
+module Termination where
+
+{- Message from Jim Apple to Haskell-Cafe, 7/1/07
+
+To show how expressive GADTs are, the datatype Terminating can hold
+any term in the untyped lambda calculus that terminates, and none that
+don't. I don't think that an encoding of this is too surprising, but I
+thought it might be a good demonstration of the power that GADTs
+bring.
+
+
+ Using GADTs to encode normalizable and non-normalizable terms in
+ the lambda calculus. For definitions of normalizable and de Bruin
+ indices, I used:
+
+ Christian Urban and Stefan Berghofer - A Head-to-Head Comparison of
+ de Bruijn Indices and Names. In Proceedings of the International
+ Workshop on Logical Frameworks and Meta-Languages: Theory and
+ Practice (LFMTP 2006). Seattle, USA. ENTCS. Pages 46-59
+
+ http://www4.in.tum.de/~urbanc/Publications/lfmtp-06.ps
+
+ @incollection{ pierce97foundational,
+ author = "Benjamin Pierce",
+ title = "Foundational Calculi for Programming Languages",
+ booktitle = "The Computer Science and Engineering Handbook",
+ publisher = "CRC Press",
+ address = "Boca Raton, FL",
+ editor = "Allen B. Tucker",
+ year = "1997",
+ url = "citeseer.ist.psu.edu/pierce95foundational.html"
+ }
+
+> So it sounds to me like the (terminating) type checker solves the
+> halting problem. Can you please explain which part of this I have
+> misunderstood?
+
+The Terminating datatype takes three parameters:
+1. A term in the untyped lambda calculus
+2. A sequence of beta reductions
+3. A proof that the result of the beta reductions is normalized.
+
+Number 2 is the hard part. For a term that calculated the factorial of
+5, the list in part 2 would be at least 120 items long, and each one
+is kind of a pain.
+
+GHC's type checker ends up doing exactly what it was doing before:
+checking proofs.
+
+-}
+
+
+-- Terms in the untyped lambda-calculus with the de Bruijn representation
+
+data Term t where
+ Var :: Nat n -> Term (Var n)
+ Lambda :: Term t -> Term (Lambda t)
+ Apply :: Term t1 -> Term t2 -> Term (Apply t1 t2)
+
+-- Natural numbers
+
+data Nat n where
+ Zero :: Nat Z
+ Succ :: Nat n -> Nat (S n)
+
+data Z
+data S n
+
+data Var t
+data Lambda t
+data Apply t1 t2
+
+data Less n m where
+ LessZero :: Less Z (S n)
+ LessSucc :: Less n m -> Less (S n) (S m)
+
+data Equal a b where
+ Equal :: Equal a a
+
+data Plus a b c where
+ PlusZero :: Plus Z b b
+ PlusSucc :: Plus a b c -> Plus (S a) b (S c)
+
+{- We can reduce a term by function application, reduction under the lambda,
+ or reduction of either side of an application. We don't need this full
+ power, since we could get by with normal-order evaluation, but that
+ required a more complicated datatype for Reduce.
+-}
+data Reduce t1 t2 where
+ ReduceSimple :: Replace Z t1 t2 t3 -> Reduce (Apply (Lambda t1) t2) t3
+ ReduceLambda :: Reduce t1 t2 -> Reduce (Lambda t1) (Lambda t2)
+ ReduceApplyLeft :: Reduce t1 t2 -> Reduce (Apply t1 t3) (Apply t2 t3)
+ ReduceApplyRight :: Reduce t1 t2 -> Reduce (Apply t3 t1) (Apply t3 t2)
+
+{- Lift and Replace use the de Bruijn operations as expressed in the Urban
+ and Berghofer paper. -}
+data Lift n k t1 t2 where
+ LiftVarLess :: Less i k -> Lift n k (Var i) (Var i)
+ LiftVarGTE :: Either (Equal i k) (Less k i) -> Plus i n r -> Lift n k (Var i) (Var r)
+ LiftApply :: Lift n k t1 t1' -> Lift n k t2 t2' -> Lift n k (Apply t1 t2) (Apply t1' t2')
+ LiftLambda :: Lift n (S k) t1 t2 -> Lift n k (Lambda t1) (Lambda t2)
+
+data Replace k t n r where
+ ReplaceVarLess :: Less i k -> Replace k (Var i) n (Var i)
+ ReplaceVarEq :: Equal i k -> Lift k Z n r -> Replace k (Var i) n r
+ ReplaceVarMore :: Less k (S i) -> Replace k (Var (S i)) n (Var i)
+ ReplaceApply :: Replace k t1 n r1 -> Replace k t2 n r2 -> Replace k (Apply t1 t2) n (Apply r1 r2)
+ ReplaceLambda :: Replace (S k) t n r -> Replace k (Lambda t) n (Lambda r)
+
+{- Reflexive transitive closure of the reduction relation. -}
+data ReduceEventually t1 t2 where
+ ReduceZero :: ReduceEventually t1 t1
+ ReduceSucc :: Reduce t1 t2 -> ReduceEventually t2 t3 -> ReduceEventually t1 t3
+
+-- Definition of normal form: nothing with a lambda term applied to anything.
+data Normal t where
+ NormalVar :: Normal (Var n)
+ NormalLambda :: Normal t -> Normal (Lambda t)
+ NormalApplyVar :: Normal t -> Normal (Apply (Var i) t)
+ NormalApplyApply :: Normal (Apply t1 t2) -> Normal t3 -> Normal (Apply (Apply t1 t2) t3)
+
+-- Something is terminating when it reduces to something normal
+data Terminating where
+ Terminating :: Term t -> ReduceEventually t t' -> Normal t' -> Terminating
+
+{- We can encode terms that are non-terminating, even though this set is
+ only co-recursively enumerable, so we can't actually prove all of the
+ non-normalizable terms of the lambda calculus are non-normalizable.
+-}
+
+data Reducible t1 where
+ Reducible :: Reduce t1 t2 -> Reducible t1
+-- A term is non-normalizable if, no matter how many reductions you have applied,
+-- you can still apply one more.
+type Infinite t1 = forall t2 . ReduceEventually t1 t2 -> Reducible t2
+
+data NonTerminating where
+ NonTerminating :: Term t -> Infinite t -> NonTerminating
+
+-- x
+test1 :: Terminating
+test1 = Terminating (Var Zero) ReduceZero NormalVar
+
+-- (\x . x)@y
+test2 :: Terminating
+test2 = Terminating (Apply (Lambda (Var Zero))(Var Zero))
+ (ReduceSucc (ReduceSimple (ReplaceVarEq Equal (LiftVarGTE (Left Equal) PlusZero))) ReduceZero)
+ NormalVar
+
+-- omega = \x.x@x
+type Omega = Lambda (Apply (Var Z) (Var Z))
+omega = Lambda (Apply (Var Zero) (Var Zero))
+
+-- (\x . \y . y)@(\z.z@z)
+test3 :: Terminating
+test3 = Terminating (Apply (Lambda (Lambda (Var Zero))) omega)
+ (ReduceSucc (ReduceSimple (ReplaceLambda (ReplaceVarLess LessZero))) ReduceZero)
+ (NormalLambda NormalVar)
+
+-- (\x.x@x)(\x.x@x)
+test4 :: NonTerminating
+test4 = NonTerminating (Apply omega omega) help3
+
+help1 :: Reducible (Apply Omega Omega)
+help1 = Reducible (ReduceSimple
+ (ReplaceApply (ReplaceVarEq Equal (LiftLambda
+ (LiftApply (LiftVarLess LessZero) (LiftVarLess LessZero))))
+ (ReplaceVarEq Equal (LiftLambda (LiftApply
+ (LiftVarLess LessZero) (LiftVarLess LessZero))))))
+
+help2 :: ReduceEventually (Apply Omega Omega) t -> Equal (Apply Omega Omega) t
+help2 ReduceZero = Equal
+help2 (ReduceSucc (ReduceSimple (ReplaceApply
+ (ReplaceVarEq _ (LiftLambda (LiftApply (LiftVarLess _) (LiftVarLess _))))
+ (ReplaceVarEq _ (LiftLambda (LiftApply (LiftVarLess _) (LiftVarLess _)))))) y)
+ = case help2 y of
+ Equal -> Equal
+
+help3 :: Infinite (Apply Omega Omega)
+help3 x = case help2 x of
+ Equal -> help1