on master help sign in
Added 'quoteGoal x by p in e' syntax

meaning, let x = p [current goal] in e Additionally, the value of x is normalised, which means p can be a compile-time only %reflection function used to prove something interesting in e. See test030 for an example of automated proof by reflection.

commit48d110594cf7b1cb21adbf1f4a3e55185a10d4d2 authorEdwin Brady <ecb10@st-andrews.ac.uk> dateMon 5 Aug 2013, 00:46 (13 years ago) parentfd9bd68
15 files changed+308−1
CHANGELOG+2 src/Core/CoreParser.hs+1 src/Core/Elaborate.hs+3 src/Core/ProofState.hs+11 src/Idris/AbsSyntax.hs+3 src/Idris/AbsSyntaxTree.hs+2 src/Idris/DSL.hs+2 src/Idris/ElabTerm.hs+17−1 src/Idris/IBC.hs+18 src/Idris/Parser.hs+10 test/test030/Reflect.idr+205 test/test030/expected+4 test/test030/run+4 test/test030/test030.idr+13 test/test030/test030a.idr+13
CHANGELOG+2
@@ -3,6 +3,8 @@ New in 0.9.10:
33
44* Add '%reflection' option for functions, for compile-time operations on
55 syntax.
6+* Add 'quoteGoal x by p in e' which makes x equal to p applied to the goal
7+ in the scope e
68* At the REPL, "it" now refers to the previous expression
79
810New in 0.9.9:
src/Core/CoreParser.hs+1
@@ -35,6 +35,7 @@ idrisDef = haskellDef {
3535 "where", "with", "syntax", "proof", "postulate",
3636 "using", "namespace", "class", "instance",
3737 "public", "private", "abstract", "implicit",
38+ "quoteGoal",
3839 "Int", "Integer", "Float", "Char", "String", "Ptr",
3940 "Bits8", "Bits16", "Bits32", "Bits64",
4041 "Bits8x16", "Bits16x8", "Bits32x4", "Bits64x2"]
src/Core/Elaborate.hs+3
@@ -263,6 +263,9 @@ regret = processTactic' Regret
263263compute :: Elab' aux ()
264264compute = processTactic' Compute
265265
266+computeLet :: Name -> Elab' aux ()
267+computeLet n = processTactic' (ComputeLet n)
268+
266269simplify :: Elab' aux ()
267270simplify = processTactic' Simplify
268271
src/Core/ProofState.hs+11
@@ -58,6 +58,7 @@ data Tactic = Attack
5858 | StartUnify Name
5959 | EndUnify
6060 | Compute
61+ | ComputeLet Name
6162 | Simplify
6263 | HNF_Compute
6364 | EvalIn Raw
@@ -265,6 +266,14 @@ tactic h f = do ps <- get
265266 atHb c env t = do ty' <- atH c env (binderTy t)
266267 return $ t { binderTy = ty' }
267268
269+computeLet :: Context -> Name -> Term -> Term
270+computeLet ctxt n tm = cl [] tm where
271+ cl env (Bind n' (Let t v) sc)
272+ | n' == n = Bind n' (Let t (normalise ctxt env v)) sc
273+ cl env (Bind n' b sc) = Bind n' (fmap (cl env) b) (cl ((n, b):env) sc)
274+ cl env (App f a) = App (cl env f) (cl env a)
275+ cl env t = t
276+
268277attack :: RunTactic
269278attack ctxt env (Bind x (Hole t) sc)
270279 = do h <- getName "hole"
@@ -698,6 +707,8 @@ processTactic EndUnify ps
698707processTactic (Reorder n) ps
699708 = do ps' <- execStateT (tactic (Just n) reorder_claims) ps
700709 return (ps' { previous = Just ps, plog = "" }, plog ps')
710+processTactic (ComputeLet n) ps
711+ = return (ps { pterm = computeLet (context ps) n (pterm ps) }, "")
701712processTactic t ps
702713 = case holes ps of
703714 [] -> fail "Nothing to fill in."
src/Idris/AbsSyntax.hs+3
@@ -1023,6 +1023,9 @@ addImpl' inpat env infns ist ptm = ai (zip env (repeat Nothing)) ptm
10231023 ai env (PPi p n ty sc) = let ty' = ai env ty
10241024 sc' = ai ((n, Just ty):env) sc in
10251025 PPi p n ty' sc'
1026+ ai env (PGoal fc r n sc) = let r' = ai env r
1027+ sc' = ai ((n, Nothing):env) sc in
1028+ PGoal fc r' n sc'
10261029 ai env (PHidden tm) = PHidden (ai env tm)
10271030 ai env (PProof ts) = PProof (map (fmap (ai env)) ts)
10281031 ai env (PTactics ts) = PTactics (map (fmap (ai env)) ts)
src/Idris/AbsSyntaxTree.hs+2
@@ -524,6 +524,7 @@ data PTerm = PQuote Raw
524524 | PAlternative Bool [PTerm] -- True if only one may work
525525 | PHidden PTerm -- ^ Irrelevant or hidden pattern
526526 | PType
527+ | PGoal FC PTerm Name PTerm
527528 | PConstant Const
528529 | Placeholder
529530 | PDoBlock [PDo]
@@ -560,6 +561,7 @@ mapPT f t = f (mpt t) where
560561 mpt (PProof ts) = PProof (map (fmap (mapPT f)) ts)
561562 mpt (PTactics ts) = PTactics (map (fmap (mapPT f)) ts)
562563 mpt (PUnifyLog tm) = PUnifyLog (mapPT f tm)
564+ mpt (PGoal fc r n sc) = PGoal fc (mapPT f r) n (mapPT f sc)
563565 mpt x = x
564566
565567
src/Idris/DSL.hs+2
@@ -40,6 +40,8 @@ expandDo dsl (PHidden t) = PHidden (expandDo dsl t)
4040expandDo dsl (PReturn fc) = dsl_return dsl
4141expandDo dsl (PRewrite fc r t ty)
4242 = PRewrite fc r (expandDo dsl t) ty
43+expandDo dsl (PGoal fc r n sc)
44+ = PGoal fc (expandDo dsl r) n (expandDo dsl sc)
4345expandDo dsl (PDoBlock ds) = expandDo dsl $ block (dsl_bind dsl) ds
4446 where
4547 block b [DoExp fc tm] = tm
src/Idris/ElabTerm.hs+17−1
@@ -292,6 +292,22 @@ elab ist info pattern tcgen fn tm
292292 expandLet n (case lookup n env of
293293 Just (Let t v) -> v)
294294 solve
295+ elab' ina@(_,a) (PGoal fc r n sc) = do
296+ rty <- goal
297+ attack
298+ tyn <- unique_hole (MN 0 "letty")
299+ claim tyn RType
300+ valn <- unique_hole (MN 0 "letval")
301+ claim valn (Var tyn)
302+ letbind n (Var tyn) (Var valn)
303+ focus valn
304+ elabE (True, a) (PApp fc r [pexp (delab ist rty)])
305+ computeLet n
306+ env <- get_env
307+ elabE (True, a) sc
308+ solve
309+-- elab' ina (PLet n Placeholder
310+-- (PApp fc r [pexp (delab ist rty)]) sc)
295311 elab' ina tm@(PApp fc (PInferRef _ f) args) = do
296312 rty <- goal
297313 ds <- get_deferred
@@ -1277,9 +1293,9 @@ mkSpecialised :: IState -> FC -> Name -> [PTerm] -> PTerm -> ElabD PTerm
12771293mkSpecialised i fc n args def
12781294 = do let tm' = def
12791295 case lookupCtxt n (idris_statics i) of
1280− [] -> return tm'
12811296 [as] -> if (not (or as)) then return tm' else
12821297 mkSpecDecl i n (zip args as) tm'
1298+ _ -> return tm'
12831299
12841300mkSpecDecl :: IState -> Name -> [(PTerm, Bool)] -> PTerm -> ElabD PTerm
12851301mkSpecDecl i n pargs tm'
src/Idris/IBC.hs+18
@@ -662,6 +662,10 @@ instance Binary CaseAlt where
662662 put x2
663663 DefaultCase x1 -> do putWord8 2
664664 put x1
665+ FnCase x1 x2 x3 -> do putWord8 3
666+ put x1
667+ put x2
668+ put x3
665669 get
666670 = do i <- getWord8
667671 case i of
@@ -675,6 +679,10 @@ instance Binary CaseAlt where
675679 return (ConstCase x1 x2)
676680 2 -> do x1 <- get
677681 return (DefaultCase x1)
682+ 3 -> do x1 <- get
683+ x2 <- get
684+ x3 <- get
685+ return (FnCase x1 x2 x3)
678686 _ -> error "Corrupted binary data for CaseAlt"
679687
680688
@@ -1321,6 +1329,11 @@ instance Binary PTerm where
13211329 put x2
13221330 put x3
13231331 put x4
1332+ PGoal x1 x2 x3 x4 -> do putWord8 31
1333+ put x1
1334+ put x2
1335+ put x3
1336+ put x4
13241337 get
13251338 = do i <- getWord8
13261339 case i of
@@ -1410,6 +1423,11 @@ instance Binary PTerm where
14101423 x3 <- get
14111424 x4 <- get
14121425 return (PRewrite x1 x2 x3 x4)
1426+ 31 -> do x1 <- get
1427+ x2 <- get
1428+ x3 <- get
1429+ x4 <- get
1430+ return (PGoal x1 x2 x3 x4)
14131431 _ -> error "Corrupted binary data for PTerm"
14141432
14151433instance (Binary t) => Binary (PTactic' t) where
src/Idris/Parser.hs+10
@@ -621,6 +621,7 @@ pNoExtExpr syn =
621621 <|> pRecordType syn
622622 <|> try (pSimpleExpr syn)
623623 <|> pLambda syn
624+ <|> pQuoteGoal syn
624625 <|> pLet syn
625626 <|> pRewriteTerm syn
626627 <|> pPi syn
@@ -681,6 +682,7 @@ pExt syn (Rule ssym ptm _)
681682 : upd ns ds
682683 upd ns (DoLetP fc i t : ds) = DoLetP fc (update ns i) (update ns t)
683684 : upd ns ds
685+ update ns (PGoal fc r n sc) = PGoal fc (update ns r) n (update ns sc)
684686 update ns t = t
685687
686688pName = do i <- getState
@@ -1009,6 +1011,14 @@ pLet syn = try (do reserved "let"; n <- pName;
10091011 reserved "in"; sc <- pExpr syn
10101012 return (PCase fc v [(pat, sc)]))
10111013
1014+pQuoteGoal syn = do reserved "quoteGoal"; n <- pName;
1015+ reserved "by"
1016+ r <- pExpr syn
1017+ reserved "in"
1018+ fc <- pfc
1019+ sc <- pExpr syn
1020+ return (PGoal fc r n sc)
1021+
10121022pPi syn =
10131023 try (do lazy <- if implicitAllowed syn -- laziness is top level only
10141024 then option False (do lchar '|'; return True)
test/test030/Reflect.idrnew file+205
@@ -0,0 +1,205 @@
1+module Reflect
2+
3+import Decidable.Equality
4+
5+using (xs : List a, ys : List a, G : List (List a))
6+
7+ data Elem : a -> List a -> Type where
8+ Stop : Elem x (x :: xs)
9+ Pop : Elem x ys -> Elem x (y :: xs)
10+
11+-- Expr is a reflection of a list, indexed over the concrete list,
12+-- and over a set of list variables.
13+
14+ data Expr : List (List a) -> List a -> Type where
15+ App : Expr G xs -> Expr G ys -> Expr G (xs ++ ys)
16+ Var : Elem xs G -> Expr G xs
17+ ENil : Expr G []
18+
19+-- Reflection of list equalities, indexed over the concrete equality.
20+
21+ data ListEq : List (List a) -> Type -> Type where
22+ EqP : Expr G xs -> Expr G ys -> ListEq G (xs = ys)
23+
24+-- Fully right associative list expressions
25+
26+ data RExpr : List (List a) -> List a -> Type where
27+ RApp : RExpr G xs -> Elem ys G -> RExpr G (xs ++ ys)
28+ RNil : RExpr G []
29+
30+-- Convert an expression to a right associative expression, and return
31+-- a proof that the rewriting has an equal interpretation to the original
32+-- expression.
33+
34+-- The idea is that we use this proof to build a proof of equality of
35+-- list appends
36+
37+ expr_r : Expr G xs -> (xs' ** (RExpr G xs', xs = xs'))
38+ expr_r ENil = (_ ** (RNil, refl))
39+ expr_r (Var i) = (_ ** (RApp RNil i, refl))
40+ expr_r (App ex ey) = let (xl ** (xr, xprf)) = expr_r ex in
41+ let (yl ** (yr, yprf)) = expr_r ey in
42+ appRExpr _ _ xr yr xprf yprf
43+ where
44+ appRExpr : (xs', ys' : List a) ->
45+ RExpr G xs -> RExpr G ys -> (xs' = xs) -> (ys' = ys) ->
46+ (ws' ** (RExpr G ws', xs' ++ ys' = ws'))
47+ appRExpr x' y' rxs (RApp e i) xprf yprf
48+ = let (xs ** (rec, prf)) = appRExpr _ _ rxs e refl refl in
49+ (_ ** (RApp rec i, ?appRExpr1))
50+ appRExpr x' y' rxs RNil xprf yprf = (_ ** (rxs, ?appRExpr2))
51+
52+ r_expr : RExpr G xs -> Expr G xs
53+ r_expr RNil = ENil
54+ r_expr (RApp xs i) = App (r_expr xs) (Var i)
55+
56+-- Convert an expression to some other equivalent expression (which
57+-- just happens to be normalised to right associative form)
58+
59+ reduce : Expr G xs -> (xs' ** (Expr G xs', xs = xs'))
60+ reduce e = let (x' ** (e', prf)) = expr_r e in
61+ (x' ** (r_expr e', prf))
62+
63+-- Build a proof that two expressions are equal. If they are, we'll know
64+-- that the indices are equal.
65+
66+ eqExpr : (e : Expr G xs) -> (e' : Expr G ys) ->
67+ Maybe (e = e')
68+ eqExpr (App x y) (App x' y') with (eqExpr x x', eqExpr y y')
69+ eqExpr (App x y) (App x y) | (Just refl, Just refl) = Just refl
70+ eqExpr (App x y) (App x' y') | _ = Nothing
71+ eqExpr (Var i) (Var j) with (prim__syntactic_eq _ _ i j)
72+ eqExpr (Var i) (Var i) | (Just refl) = Just refl
73+ eqExpr (Var i) (Var j) | _ = Nothing
74+ eqExpr ENil ENil = Just refl
75+ eqExpr _ _ = Nothing
76+
77+-- Given a couple of reflected expressions, try to produce a proof that
78+-- they are equal
79+
80+ buildProof : {xs : List a} -> {ys : List a} ->
81+ Expr G ln -> Expr G rn ->
82+ (xs = ln) -> (ys = rn) -> Maybe (xs = ys)
83+ buildProof e e' lp rp with (eqExpr e e')
84+ buildProof e e lp rp | Just refl = ?bp1
85+ buildProof e e' lp rp | Nothing = Nothing
86+
87+ testEq : Expr G xs -> Expr G ys -> Maybe (xs = ys)
88+ testEq l r = let (ln ** (l', lPrf)) = reduce l in
89+ let (rn ** (r', rPrf)) = reduce r in
90+ buildProof l' r' lPrf rPrf
91+
92+-- Given a reflected equality, try to produce a proof that holds
93+
94+ prove : ListEq G t -> Maybe t
95+ prove (EqP xs ys) = testEq xs ys
96+
97+ getJust : (x : Maybe a) -> IsJust x -> a
98+ getJust (Just p) ItIsJust = p
99+
100+
101+-- Some helpers for later... 'prim__syntactic_eq' is a primitive which
102+-- (at compile-time only) attempts to construct a proof that its arguments
103+-- are syntactically equal. We'll find this useful for referring to variables
104+-- in reflected terms.
105+
106+ isElem : (x : a) -> (xs : List a) -> Maybe (Elem x xs)
107+ isElem x [] = Nothing
108+ isElem x (y :: ys) with (prim__syntactic_eq _ _ x y)
109+ isElem x (x :: ys) | Just refl = [| Stop |]
110+ isElem x (y :: ys) | Nothing = [| Pop (isElem x ys) |]
111+
112+ weakenElem : (G' : List a) -> Elem x xs -> Elem x (G' ++ xs)
113+ weakenElem [] p = p
114+ weakenElem (g :: G) p = Pop (weakenElem G p)
115+
116+ weaken : (G' : List (List a)) ->
117+ Expr G xs -> Expr (G' ++ G) xs
118+ weaken G' (App l r) = App (weaken G' l) (weaken G' r)
119+ weaken G' (Var x) = Var (weakenElem G' x)
120+ weaken G' ENil = ENil
121+
122+
123+-- Now, some reflection magic.
124+-- %reflection means a function runs on syntax, rather than on constructors.
125+-- So, 'reflectList' builds a reflected List expression as defined above.
126+
127+-- It also converts (x :: xs) into a reflected [x] ++ xs so that the above
128+-- reduction will work the right way.
129+
130+%reflection
131+reflectList : (G : List (List a)) ->
132+ (xs : List a) -> (G' ** Expr (G' ++ G) xs)
133+reflectList G [] = ([] ** ENil)
134+reflectList G (x :: xs) with (reflectList G xs)
135+ | (G' ** xs') with (isElem (List.(::) x []) (G' ++ G))
136+ | Just p = (G' ** App (Var p) xs')
137+ | Nothing = ([x] :: G' ** App (Var Stop) (weaken [[x]] xs'))
138+reflectList G (xs ++ ys) with (reflectList G xs)
139+ | (G' ** xs') with (reflectList (G' ++ G) ys)
140+ | (G'' ** ys') = ((G'' ++ G') **
141+ rewrite (sym (appendAssociative G'' G' G)) in
142+ App (weaken G'' xs') ys')
143+reflectList G t with (isElem t G)
144+ | Just p = ([] ** Var p)
145+ | Nothing = ([t] ** Var Stop)
146+
147+
148+-- Similarly, reflectEq converts an equality proof on Lists into the ListEq
149+-- reflection. Note that it isn't total, and we have to give the element type
150+-- explicitly because it can't be inferred from P.
151+
152+-- This is not really a problem - we'll want different reflections for different
153+-- forms of equality proofs anyway.
154+
155+%reflection
156+reflectEq : (a : Type) -> (G : List (List a)) -> (P : Type) -> (G' ** ListEq (G' ++ G) P)
157+reflectEq a G (the (List a) xs = the (List a) ys) with (reflectList G xs)
158+ | (G' ** xs') with (reflectList (G' ++ G) ys)
159+ | (G'' ** ys') = ((G'' ++ G') **
160+ rewrite (sym (appendAssociative G'' G' G)) in
161+ EqP (weaken G'' xs') ys')
162+
163+
164+-- Need these before we can test it or the reductions won't normalise fully...
165+
166+---------- Proofs ----------
167+
168+Reflect.appRExpr1 = proof {
169+ intros;
170+ rewrite sym xprf;
171+ rewrite sym yprf;
172+ rewrite prf;
173+ rewrite sym (appendAssociative xs xs2 ys1);
174+ trivial;
175+}
176+
177+Reflect.appRExpr2 = proof {
178+ intros;
179+ rewrite xprf;
180+ rewrite sym yprf;
181+ rewrite appendNilRightNeutral x';
182+ trivial;
183+}
184+
185+Reflect.bp1 = proof {
186+ intros;
187+ refine Just;
188+ rewrite sym lp;
189+ rewrite sym rp;
190+ trivial;
191+}
192+
193+-- "quoteGoal x by p in e" does some magic
194+-- The effect is to bind x to p applied to the current goal. If 'p' is a
195+-- reflection function (which is the most likely thing to be useful...)
196+-- then we can feed the result to the above 'prove' function and pull out
197+-- the proof, if it exists.
198+
199+-- The syntax declaration below just gives us an easy way to invoke the
200+-- prover.
201+
202+syntax AssocProof [ty] = quoteGoal x by reflectEq ty [] in
203+ getJust (prove (getProof x)) ItIsJust
204+
205+
test/test030/expectednew file+4
@@ -0,0 +1,4 @@
1+test030a.idr:12:Can't unify IsJust (Just x) with IsJust (prove (getProof x))
2+
3+Specifically:
4+ Can't unify Just x with Nothing
test/test030/runnew file+4
@@ -0,0 +1,4 @@
1+#!/bin/bash
2+idris $@ test030.idr --check
3+idris $@ test030a.idr --check
4+rm -f *.ibc
test/test030/test030.idrnew file+13
@@ -0,0 +1,13 @@
1+module test030
2+
3+import Reflect
4+
5+total
6+testReflect0 : (xs, ys : List a) -> ((xs ++ (ys ++ xs)) = ((xs ++ ys) ++ xs))
7+testReflect0 {a} xs ys = AssocProof a
8+
9+total
10+testReflect1 : (xs, ys : List a) ->
11+ ((xs ++ (x :: ys ++ xs)) = ((xs ++ [x]) ++ (ys ++ xs)))
12+testReflect1 {a} xs ys = AssocProof a
13+
test/test030/test030a.idrnew file+13
@@ -0,0 +1,13 @@
1+module test030
2+
3+import Reflect
4+
5+total
6+testReflect0 : (xs, ys : List a) -> ((xs ++ (ys ++ xs)) = ((xs ++ ys) ++ xs))
7+testReflect0 {a} xs ys = AssocProof a
8+
9+total
10+testReflect1 : (xs, ys : List a) ->
11+ ((ys ++ (x :: ys ++ xs)) = ((xs ++ [x]) ++ (ys ++ xs)))
12+testReflect1 {a} xs ys = AssocProof a
13+