Skip to content

Commit 2dcd269

Browse files
author
Alex Gryzlov
committed
Tactics: applyIn, some of suggested changes
1 parent 695c423 commit 2dcd269

File tree

1 file changed

+92
-46
lines changed

1 file changed

+92
-46
lines changed

src/Tactics.lidr

Lines changed: 92 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ will see:
1919

2020
> import Basics
2121

22-
import Poly
22+
\todo[inline]{If we \idr{import Poly} here, the pair sugar, among other things,
23+
will start messing things up, so we just copypaste the necessary definitions for
24+
now}
2325

2426
\todo[inline]{Describe \idr{Pruviloj} and \idr{%runElab}}
2527

@@ -31,9 +33,6 @@ will see:
3133

3234
> %language ElabReflection
3335

34-
%hide Poly.MumbleGrumble.A
35-
%hide Poly.MumbleGrumble.B
36-
3736

3837
== The \idr{exact} Tactic
3938

@@ -42,11 +41,17 @@ as some hypothesis in the context or some previously proved lemma.
4241

4342
> silly1 : (n, m, o, p : Nat) -> (n = m) -> [n,o] = [n,p] -> [n,o] = [m,p]
4443

45-
Here, we could prove this with
44+
Here, we could prove this with rewrites:
4645

4746
> silly1 n m o p eq1 eq2 = rewrite eq2 in
4847
> rewrite eq1 in Refl
4948

49+
or the dependent pattern matching:
50+
51+
```idris
52+
silly1 m m p p Refl Refl = Refl
53+
```
54+
5055
as we have done several times before. We can achieve the same effect by a series
5156
of tactic applications, using \idr{exact} instead of the
5257
\idr{rewrite}+\idr{Refl} combination:
@@ -160,11 +165,15 @@ tactic, which switches the left and right sides of an equality in the goal.
160165
(_Hint_: You can use \idr{exact} with previously defined lemmas, not just
161166
hypotheses in the context. Remember that `:search` is your friend.)
162167

163-
rev_exercise1 : (l, l' : List Nat) -> l = rev l' -> l' = rev l
164-
rev_exercise1 = ?remove_me1 -- %runElab rev_exercise1_tac
165-
where
166-
rev_exercise1_tac : Elab ()
167-
rev_exercise1_tac = ?rev_exercise1_tac_rhs
168+
> rev : (l : List x) -> List x
169+
> rev [] = []
170+
> rev (h::t) = (rev t) ++ [h]
171+
172+
> rev_exercise1 : (l, l' : List Nat) -> l = rev l' -> l' = rev l
173+
> rev_exercise1 = ?remove_me1 -- %runElab rev_exercise1_tac
174+
> where
175+
> rev_exercise1_tac : Elab ()
176+
> rev_exercise1_tac = ?rev_exercise1_tac_rhs
168177

169178
$\square$
170179

@@ -189,12 +198,17 @@ The following silly example uses two rewrites in a row to get from `[a,b]` to
189198
> trans_eq_example a b c d e f eq1 eq2 = rewrite eq1 in
190199
> rewrite eq2 in Refl
191200

201+
Note that this can also be proven with dependent pattern matching:
202+
203+
```idris
204+
trans_eq_example a b a b a b Refl Refl = Refl
205+
```
206+
192207
Since this is a common pattern, we might like to pull it out as a lemma
193208
recording, once and for all, the fact that equality is transitive.
194209

195210
> trans_eq : (n, m, o : x) -> n = m -> m = o -> n = o
196-
> trans_eq n m o eq1 eq2 = rewrite eq1 in
197-
> rewrite eq2 in Refl
211+
> trans_eq n n n Refl Refl = Refl
198212

199213
(This lemma already exists in Idris' stdlib under the name of \idr{trans}.)
200214

@@ -302,6 +316,12 @@ derived at once.
302316

303317
\todo[inline]{Make prettier and contribute to Pruviloj?}
304318

319+
> getTTType : Raw -> Elab TT
320+
> getTTType r = do
321+
> env <- getEnv
322+
> rty <- check env r
323+
> pure $ snd rty
324+
305325
> symmetryIn : Raw -> TTName -> Elab ()
306326
> symmetryIn t n = do
307327
> tt <- getTTType t
@@ -315,12 +335,6 @@ derived at once.
315335
> tsymty <- forget !(getTTType tsym)
316336
> letbind n tsymty tsym
317337
> _ => fail [TermPart tt, TextPart "is not an equality"]
318-
> where
319-
> getTTType : Raw -> Elab TT
320-
> getTTType r = do
321-
> env <- getEnv
322-
> rty <- check env r
323-
> pure $ snd rty
324338

325339
\todo[inline]{Works quite fast in Elab shell but hangs the compiler}
326340

@@ -398,13 +412,16 @@ things!
398412
\todo[inline]{How to show impossible cases from Elab?}
399413

400414
> inversion_ex4 : (n : Nat) -> S n = Z -> 2 + 2 = 5
401-
> inversion_ex4 n prf = absurd $ SIsNotZ prf
415+
> -- we need "sym" because Prelude.Nat only disproves "Z = S n" for us
416+
> inversion_ex4 n prf = absurd $ sym prf
417+
418+
\todo[inline]{Remove if https://github.com/idris-lang/Idris-dev/pull/3925 is merged}
402419

403-
> FalseNotTrue : False = True -> Void
404-
> FalseNotTrue Refl impossible
420+
> Uninhabited (False = True) where
421+
> uninhabited Refl impossible
405422

406423
> inversion_ex5 : (n, m : Nat) -> False = True -> [n] = [m]
407-
> inversion_ex5 n m prf = absurd $ FalseNotTrue prf
424+
> inversion_ex5 n m prf = absurd prf
408425

409426
If you find the principle of explosion confusing, remember that these proofs are
410427
not actually showing that the conclusion of the statement holds. Rather, they
@@ -450,7 +467,7 @@ general fact about both constructors and functions, which we will find useful in
450467
a few places below:
451468

452469
> f_equal : (f : a -> b) -> (x, y : a) -> x = y -> f x = f y
453-
> f_equal f x y eq = rewrite eq in Refl
470+
> f_equal f x x Refl = Refl
454471

455472
(This is called \idr{cong} in Idris' stdlib.)
456473

@@ -474,25 +491,50 @@ in H performs simplification in the hypothesis named \idr{h} in the context.
474491
> | _ => fail []
475492
> exact $ Var eq
476493

477-
\todo[inline]{Write \idr{applyIn} and reformulate the example as it's trivial in
478-
Idris}
494+
\todo[inline]{Contribute to Pruviloj}
495+
496+
> applyIn : Raw -> Raw -> TTName -> Elab ()
497+
> applyIn f x n = do
498+
> ftt <- getTTType f
499+
> case ftt of
500+
> `(~xty -> ~yty) => do
501+
> let fx = RApp f x
502+
> fxty <- forget !(getTTType fx)
503+
> letbind n fxty fx
504+
> _ => fail [TermPart ftt, TextPart "is not a function"]
505+
506+
\todo[inline]{Edit}
479507

480-
Similarly, apply L in H matches some conditional statement L (of the form L1 ->
481-
L2, say) against a hypothesis H in the context. However, unlike ordinary apply
482-
(which rewrites a goal matching L2 into a subgoal L1), apply L in H matches H
483-
against L1 and, if successful, replaces it with L2.
508+
Similarly, \idr{applyIn l h} matches some conditional statement \idr{l} (of the
509+
form \idr{l1 -> l2}, say) against a hypothesis \idr{h} in the context. However,
510+
unlike ordinary \idr{exact} (which rewrites a goal matching \idr{l2} into a
511+
subgoal \idr{l1}), \idr{applyIn l h n} matches \idr{h} against \idr{l1} and, if
512+
successful, binds the result \idr{l2} to \idr{n}.
484513

485-
In other words, apply L in H gives us a form of "forward reasoning": from L1 ->
486-
L2 and a hypothesis matching L1, it produces a hypothesis matching L2. By
487-
contrast, apply L is "backward reasoning": it says that if we know L1->L2 and we
488-
are trying to prove L2, it suffices to prove L1.
514+
In other words, \idr{applyIn l h} gives us a form of "forward reasoning": from
515+
\idr{l1 -> l2} and a hypothesis matching \idr{l1}, it produces a hypothesis
516+
matching \idr{l2}. By contrast, \idr{exact l} is "backward reasoning": it says
517+
that if we know \idr{l1->l2} and we are trying to prove \idr{l2}, it suffices to
518+
prove \idr{l1}.
489519

490520
Here is a variant of a proof from above, using forward reasoning throughout
491521
instead of backward reasoning.
492522

493-
> silly3' : (n : Nat) -> (beq_nat n 5 = True -> beq_nat (S (S n)) 7 = True) ->
494-
> True = beq_nat n 5 ->
495-
> True = beq_nat (S (S n)) 7
523+
> silly3' : (n, m : Nat) -> (beq_nat n 5 = True -> beq_nat m 7 = True) ->
524+
> True = beq_nat n 5 -> True = beq_nat m 7
525+
> silly3' = %runElab silly3_tac
526+
> where
527+
> silly3_tac : Elab ()
528+
> silly3_tac = do
529+
> [n,m,eq,h] <- intros
530+
> | _ => fail []
531+
> hsym <- gensym "hsym"
532+
> symmetryIn (Var h) hsym
533+
> happ <- gensym "happ"
534+
> applyIn (Var eq) (Var hsym) happ
535+
> happsym <- gensym "happsym"
536+
> symmetryIn (Var happ) happsym
537+
> exact $ Var happsym
496538

497539
Forward reasoning starts from what is _given_ (premises, previously proven
498540
theorems) and iteratively draws conclusions from them until the goal is reached.
@@ -659,11 +701,15 @@ The following exercise requires the same pattern.
659701

660702
==== Exercise: 2 stars (beq_nat_true)
661703

704+
\todo[inline]{We can't leave this undefined as \idr{beq_id_true} later depends
705+
on it. Use \idr{really_believe_me}?}
706+
662707
> beq_nat_true : beq_nat n m = True -> n = m
663708
> beq_nat_true {n=Z} {m=Z} _ = Refl
664709
> beq_nat_true {n=(S _)} {m=Z} Refl impossible
665710
> beq_nat_true {n=Z} {m=(S _)} Refl impossible
666-
> beq_nat_true {n=(S n')} {m=(S m')} eq = rewrite beq_nat_true {n=n'} {m=m'} eq in Refl
711+
> beq_nat_true {n=(S n')} {m=(S m')} eq =
712+
> rewrite beq_nat_true {n=n'} {m=m'} eq in Refl
667713

668714
$\square$
669715

@@ -673,7 +719,7 @@ $\square$
673719
Give a careful informal proof of \idr{beq_nat_true}, being as explicit as possible
674720
about quantifiers.
675721

676-
(* FILL IN HERE *)
722+
> -- FILL IN HERE
677723

678724
$\square$
679725

@@ -870,17 +916,17 @@ Similarly, tentatively unfolding \idr{bar (m+1)} leaves a match whose scrutinee
870916
is a function application (that, itself, cannot be simplified, even after
871917
unfolding the definition of \idr{+}), so \idr{Refl} leaves it alone.
872918

873-
\todo[inline]{Edit}
874-
875-
At this point, there are two ways to make progress. One is to use destruct m to
876-
break the proof into two cases, each focusing on a more concrete choice of m (Z
877-
vs S _). In each case, the match inside of bar can now make progress, and the
878-
proof is easy to complete.
919+
At this point, there are two ways to make progress. One is to match on implicit
920+
parameter \idr{m} to break the proof into two cases, each focusing on a more
921+
concrete choice of \idr{m} (\idr{Z} vs \idr{S _}). In each case, the match
922+
inside of \idr{bar} can now make progress, and the proof is easy to complete.
879923

880924
> silly_fact_2 : bar m + 1 = bar (m + 1) + 1
881925
> silly_fact_2 {m=Z} = Refl
882926
> silly_fact_2 {m=(S _)} = Refl
883927

928+
\todo[inline]{Edit}
929+
884930
This approach works, but it depends on our recognizing that the match hidden
885931
inside bar is what was preventing us from making progress. A more
886932
straightforward way to make progress is to explicitly tell Idris to unfold bar.
@@ -901,7 +947,7 @@ Qed.
901947

902948
== Using \idr{destruct} on Compound Expressions
903949

904-
\todo[inline]{Edit}
950+
\todo[inline]{Edit. Explain \idr{with}}
905951

906952
We have seen many examples where destruct is used to perform case analysis of
907953
the value of some variable. But sometimes we need to reason by cases on the
@@ -967,7 +1013,7 @@ like this:
9671013
> sillyfun1_odd (S (S (S Z))) Refl | True = Refl
9681014
> sillyfun1_odd n prf | False with (beq_nat n 5)
9691015
> sillyfun1_odd (S (S (S (S (S Z))))) Refl | False | True = Refl
970-
> sillyfun1_odd n prf | False | False = absurd $ FalseNotTrue prf
1016+
> sillyfun1_odd n prf | False | False = absurd prf
9711017

9721018
\todo[inline]{Edit the following, since \idr{with} works fine here as well}
9731019

0 commit comments

Comments
 (0)