Skip to content

Commit 6bea767

Browse files
author
Alex Gryzlov
committed
WIP Tactics (inversion/injective)
1 parent 8e92bf3 commit 6bea767

File tree

1 file changed

+168
-103
lines changed

1 file changed

+168
-103
lines changed

src/Tactics.lidr

Lines changed: 168 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ will see:
1818
> module Tactics
1919

2020
> import Basics
21-
> import Poly
21+
22+
import Poly
2223

2324
\todo[inline]{Describe \idr{Pruviloj} and \idr{%runElab}}
2425

@@ -30,6 +31,9 @@ will see:
3031

3132
> %language ElabReflection
3233

34+
%hide Poly.MumbleGrumble.A
35+
%hide Poly.MumbleGrumble.B
36+
3337

3438
== The \idr{exact} Tactic
3539

@@ -114,7 +118,10 @@ Complete the following proof without using `simpl`.
114118

115119
> silly_ex : ((n : Nat) -> evenb n = True -> oddb (S n) = True)
116120
> -> evenb 3 = True -> oddb 4 = True
117-
> silly_ex = ?silly_ex_rhs
121+
> silly_ex = ?remove_me -- %runElab silly_ex_tac
122+
> where
123+
> silly_ex_tac : Elab ()
124+
> silly_ex_tac = ?silly_ex_tac_rhs
118125

119126
$\square$
120127

@@ -129,6 +136,7 @@ right sides of the equality are swapped.
129136
> [_,_,_,_,_] <- apply (Var `{{sym}}) [True, True, True, True, False]
130137
> | _ => fail [TextPart "Failed while applying", NamePart `{symmetry} ]
131138
> solve
139+
> attack
132140

133141
> silly3_firsttry : (n : Nat) -> True = beq_nat n 5 ->
134142
> beq_nat (S (S n)) 7 = True
@@ -144,19 +152,19 @@ tactic, which switches the left and right sides of an equality in the goal.
144152

145153
> symmetry
146154
> exact $ Var h
147-
155+
> solve
148156

149157

150158
==== Exercise: 3 stars (apply_exercise1)
151159

152160
(_Hint_: You can use \idr{exact} with previously defined lemmas, not just
153161
hypotheses in the context. Remember that `:search` is your friend.)
154162

155-
> rev_exercise1 : (l, l' : List Nat) -> l = rev l' -> l' = rev l
156-
> rev_exercise1 = ?remove_me1 -- %runElab rev_exercise1_tac
157-
> where
158-
> rev_exercise1_tac : Elab ()
159-
> rev_exercise1_tac = ?rev_exercise1_tac_rhs
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
160168

161169
$\square$
162170

@@ -234,114 +242,169 @@ invocation of \idr{apply}.
234242
$\square$
235243

236244

237-
== The inversion Tactic
245+
== The \idr{inversion} Tactic
238246

239247
Recall the definition of natural numbers:
240-
Inductive Nat : Type :=
241-
| Z : Nat | S : Nat -> Nat.
248+
249+
```idris
250+
data Nat : Type where
251+
Z : Nat
252+
S : Nat -> Nat
253+
```
242254

243255
It is obvious from this definition that every number has one of two forms:
244-
either it is the constructor Z or it is built by applying the constructor S to
245-
another number. But there is more here than meets the eye: implicit in the
246-
definition (and in our informal understanding of how datatype declarations work
247-
in other programming languages) are two more facts:
256+
either it is the constructor \idr{Z} or it is built by applying the constructor
257+
\idr{S} to another number. But there is more here than meets the eye: implicit
258+
in the definition (and in our informal understanding of how datatype
259+
declarations work in other programming languages) are two more facts:
248260

249-
- The constructor S is _injective_. That is, if S n = S m, it must be the case
250-
that n = m.
261+
- The constructor \idr{S} is _injective_. That is, if \idr{S n = S m}, it must
262+
be the case that \idr{n = m}.
251263

252-
- The constructors Z and S are _disjoint_. That is, Z is not equal to S n for
253-
any n.
264+
- The constructors \idr{Z} and \idr{S} are _disjoint_. That is, \idr{Z} is not
265+
equal to \idr{S n} for any \idr{n}.
254266

255267
Similar principles apply to all inductively defined types: all constructors are
256268
injective, and the values built from distinct constructors are never equal. For
257-
lists, the cons constructor is injective and nil is different from every
258-
non-empty list. For booleans, True and False are different. (Since neither True
259-
nor False take any arguments, their injectivity is not interesting.) And so on.
269+
lists, the \idr{(::)} constructor is injective and \idr{Nil} is different from
270+
every non-empty list. For booleans, \idr{True} and \idr{False} are different.
271+
(Since neither \idr{True} nor \idr{False} take any arguments, their injectivity
272+
is not interesting.) And so on.
260273

261-
Coq provides a tactic called inversion that allows us to exploit these
262-
principles in proofs. To see how to use it, let's show explicitly that the S
263-
constructor is injective:
274+
Idris provides a tactic called \idr{injective} that allows us to exploit these
275+
principles in proofs. To see how to use it, let's show explicitly that the
276+
\idr{S} constructor is injective:
264277

265-
Theorem S_injective : ∀(n m : Nat),
266-
S n = S m -> n = m.
267-
Proof.
268-
intros n m H.
278+
> S_injective : (n, m : Nat) -> S n = S m -> n = m
279+
> S_injective = %runElab S_injective_tac
280+
> where
281+
> covering
282+
> S_injective_tac : Elab ()
283+
> S_injective_tac = do
284+
> [n, m, h] <- intros
285+
> | _ => fail []
269286

270-
By writing inversion H at this point, we are asking Coq to generate all
271-
equations that it can infer from H as additional hypotheses, replacing variables
272-
in the goal as it goes. In the present example, this amounts to adding a new
273-
hypothesis H1 : n = m and replacing n by m in the goal.
287+
By writing \idr{injective (Var h) res} at this point, we are asking Idris to
288+
generate all equations that it can infer from \idr{h} as additional hypotheses,
289+
replacing variables in the goal as it goes. In the present example, this amounts
290+
to adding a new hypothesis \idr{h1 : n = m} and replacing \idr{n} by \idr{m} in
291+
the goal.
274292

275-
inversion H. reflexivity.
276-
Qed.
293+
\todo[inline]{Explain \idr{gensym}, \idr{unproduct} and \idr{hypothesis}}
294+
295+
> res <- gensym "sInj"
296+
> injective (Var h) res
297+
> unproduct (Var res)
298+
> hypothesis
277299

278300
Here's a more interesting example that shows how multiple equations can be
279301
derived at once.
280302

281-
Theorem inversion_ex1 : ∀(n m o : Nat),
282-
[n, m] = [o, o] -> [n] = [m].
283-
Proof.
284-
intros n m o H. inversion H. reflexivity. Qed.
285-
286-
We can name the equations that inversion generates with an as ... clause:
287-
288-
Theorem inversion_ex2 : ∀(n m : Nat),
289-
[n] = [m] -> n = m.
290-
Proof.
291-
intros n m H. inversion H as [Hnm]. reflexivity. Qed.
303+
\todo[inline]{Make prettier and contribute to Pruviloj?}
304+
305+
> symmetryIn : Raw -> TTName -> Elab ()
306+
> symmetryIn t n = do
307+
> tt <- getTTType t
308+
> case tt of
309+
> `((=) {A=~A} {B=~B} ~l ~r) => do
310+
> af <- forget A
311+
> bf <- forget B
312+
> lf <- forget l
313+
> rf <- forget r
314+
> let tsym = the Raw $ `(sym {a=~af} {b=~bf} {left=~lf} {right=~rf} ~t)
315+
> tsymty <- forget !(getTTType tsym)
316+
> letbind n tsymty tsym
317+
> _ => 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
324+
325+
\todo[inline]{Works quite fast in Elab shell but hangs the compiler}
326+
327+
> -- inversion_ex1 : (n, m, o : Nat) -> [n, m] = [o, o] -> [n] = [m]
328+
> -- inversion_ex1 = %runElab inversion_ex1_tac
329+
> -- where
330+
> -- inversion_ex1_tac : Elab ()
331+
> -- inversion_ex1_tac = do
332+
> -- [n, m, o, eq] <- intros
333+
> -- | _ => fail []
334+
> -- eqinj <- gensym "eqinj"
335+
> -- injective (Var eq) eqinj
336+
> --
337+
> -- eqinj2 <- gensym "eqinj2" -- manual destructuring
338+
> -- both (Var eqinj) !(gensym "natnat") eqinj2
339+
> -- neqo <- gensym "neqo"
340+
> -- eqinj3 <- gensym "eqinj3"
341+
> -- both (Var eqinj2) no eqinj3
342+
> -- meqos <- gensym "meqos"
343+
> -- both (Var eqinj3) mos !(gensym "uu")
344+
> --
345+
> -- oeqn <- gensym "oeqn"
346+
> -- symmetryIn (Var neqo) oeqn
347+
> -- symmetry
348+
> -- rewriteWith $ Var oeqn
349+
> -- exact $ Var meqos
350+
> -- solve
351+
352+
Note that we need to pass the parameter \idr{eqinj} which will be bound to
353+
equations that \idr{injective} generates.
292354

293355

294356
==== Exercise: 1 star (inversion_ex3)
295357

296-
Example inversion_ex3 : ∀(X : Type) (x y z : X)
297-
(l j : list X),
298-
x :: y :: l = z :: j -> y :: l = x :: j -> x = y.
299-
Proof.
300-
301-
(* FILL IN HERE *) Admitted.
358+
> inversion_ex3 : (x, y, z : a) -> (l, j : List a) ->
359+
> x :: y :: l = z :: j ->
360+
> y :: l = x :: j ->
361+
> x = y
362+
> inversion_ex3 = ?remove_me3 -- %runElab inversion_ex3_tac
363+
> where
364+
> inversion_ex3_tac : Elab ()
365+
> inversion_ex3_tac = ?inversion_ex3_tac_rhs
302366

303367
$\square$
304368

305-
When used on a hypothesis involving an equality between different
306-
constructors (e.g., S n = Z), inversion solves the goal immediately. Consider
307-
the following proof:
369+
\todo[inline]{Edit or remove}
308370

309-
Theorem beq_nat_0_l : ∀n,
310-
beq_nat 0 n = True -> n = 0.
311-
Proof.
312-
intros n.
371+
When used on a hypothesis involving an equality between _different_ constructors
372+
(e.g., \idr{S n = Z}), \idr{injective} solves the goal immediately. Consider the
373+
following proof:
374+
375+
> -- beq_nat_0_l : beq_nat 0 n = True -> n = 0
313376

314377
We can proceed by case analysis on n. The first case is trivial.
315378

316-
destruct n as [| n']. - (* n = 0 *)
317-
intros H. reflexivity.
379+
> -- beq_nat_0_l {n=Z} prf = %runElab reflexivity
380+
> -- beq_nat_0_l {n=(S _)} prf = %runElab (do
318381

319-
However, the second one doesn't look so simple: assuming beq_nat 0 (S n') =
320-
True, we must show S n' = 0, but the latter clearly contradictory! The way
321-
forward lies in the assumption. After simplifying the goal state, we see that
322-
beq_nat 0 (S n') = True has become False = True:
382+
However, the second one doesn't look so simple: assuming \idr{beq_nat 0 (S n') =
383+
True}, we must show \idr{S n' = 0}, but the latter clearly contradictory! The
384+
way forward lies in the assumption. After simplifying the goal state, we see
385+
that \idr{beq_nat 0 (S n') = True} has become \idr{False = True}:
323386

324-
- (* n = S n' *)
325-
simpl.
387+
> -- compute
326388

327-
If we use inversion on this hypothesis, Coq notices that the subgoal we are
328-
working on is impossible, and therefore removes it from further consideration.
389+
\todo[inline]{Finish the script. Use \idr{disjoint}?}
329390

330-
intros H. inversion H. Qed.
391+
If we use inversion on this hypothesis, Idris notices that the subgoal we are
392+
working on is impossible, and therefore removes it from further consideration.
331393

332394
This is an instance of a logical principle known as the principle of explosion,
333-
which asserts that a contradictory hypothesis entails anything, even False
395+
which asserts that a contradictory hypothesis entails anything, even false
334396
things!
335397

336-
Theorem inversion_ex4 : ∀(n : Nat),
337-
S n = Z -> 2 + 2 = 5.
338-
Proof.
339-
intros n contra. inversion contra. Qed.
398+
\todo[inline]{How to show impossible cases from Elab?}
340399

341-
Theorem inversion_ex5 : ∀(n m : Nat),
342-
False = True -> [n] = [m].
343-
Proof.
344-
intros n m contra. inversion contra. Qed.
400+
> inversion_ex4 : (n : Nat) -> S n = Z -> 2 + 2 = 5
401+
> inversion_ex4 n prf = absurd $ SIsNotZ prf
402+
403+
> inversion_ex5 : (n, m : Nat) -> False = True -> [n] = [m]
404+
> inversion_ex5 n m prf = absurd $ FalseNotTrue prf
405+
> where
406+
> FalseNotTrue : False = True -> Void
407+
> FalseNotTrue Refl impossible
345408

346409
If you find the principle of explosion confusing, remember that these proofs are
347410
not actually showing that the conclusion of the statement holds. Rather, they
@@ -352,40 +415,42 @@ principle of explosion of more detail in the next chapter.
352415

353416
==== Exercise: 1 star (inversion_ex6)
354417

355-
Example inversion_ex6 : ∀(X : Type)
356-
(x y z : X) (l j : list X),
357-
x :: y :: l = [] -> y :: l = z :: j -> x = z.
358-
Proof.
359-
360-
(* FILL IN HERE *) Admitted.
418+
> inversion_ex6 : (x, y, z : a) -> (l, j : List a) ->
419+
> x :: y :: l = [] ->
420+
> y :: l = z :: j ->
421+
> x = z
422+
> inversion_ex6 x y z l j prf prf1 = ?inversion_ex6_rhs
361423

362424
$\square$
363425

364-
To summarize this discussion, suppose H is a hypothesis in the context
426+
To summarize this discussion, suppose \idr{h} is a hypothesis in the context
365427
or a previously proven lemma of the form
366428

367-
c a1 a2 ... an = d b1 b2 ... bm
429+
\idr{c a1 a2 ... an = d b1 b2 ... bm}
430+
431+
\todo[inline]{Edit, especially the \idr{disjoint} part}
368432

369-
for some constructors c and d and arguments a1 ... an and b1 ... bm. Then
370-
inversion H has the following effect:
433+
for some constructors \idr{c} and \idr{d} and arguments \idr{a1 ... an} and
434+
\idr{b1 ... bm}. Then \idr{injective h} and \idr{disjoint h} have the following
435+
effects:
371436

372-
- If c and d are the same constructor, then, by the injectivity of this
373-
constructor, we know that a1 = b1, a2 = b2, etc. The inversion H adds these
374-
facts to the context and tries to use them to rewrite the goal.
437+
- If \idr{c} and \idr{d} are the same constructor, then, by the injectivity of
438+
this constructor, we know that \idr{a1 = b1}, \idr{a2 = b2}, etc. The
439+
\idr{injective h} adds these facts to the context and tries to use them to
440+
rewrite the goal.
375441

376-
- If c and d are different constructors, then the hypothesis H is
377-
contradictory, and the current goal doesn't have to be considered at all. In
378-
this case, inversion H marks the current goal as completed and pops it off
379-
the goal stack.
442+
- If \idr{c} and \idr{d} are different constructors, then the hypothesis
443+
\idr{h} is contradictory, and the current goal doesn't have to be considered
444+
at all. In this case, \idr{disjoint h} marks the current goal as completed
445+
and pops it off the goal stack.
380446

381-
The injectivity of constructors allows us to reason that ∀ (n m : Nat), S n = S
382-
m -> n = m. The converse of this implication is an instance of a more general
383-
fact about both constructors and functions, which we will find useful in a few
384-
places below:
447+
The injectivity of constructors allows us to reason that \idr{(n, m : Nat) -> S
448+
n = S m -> n = m}. The converse of this implication is an instance of a more
449+
general fact about both constructors and functions, which we will find useful in
450+
a few places below:
385451

386-
Theorem f_equal : ∀(A B : Type) (f: A -> B) (x y: A),
387-
x = y -> f x = f y.
388-
Proof. intros A B f x y eq. rewrite eq. reflexivity. Qed.
452+
> 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
389454

390455

391456
== Using Tactics on Hypotheses

0 commit comments

Comments
 (0)