summaryrefslogtreecommitdiff
path: root/testsuite/tests/gadt/termination.hs
blob: be2431b8122b2baaac3b26688e9dba56c0f6cd5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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