Built with
doc-gen4, running Lean4.
Bubbles (
) indicate interactive fragments: hover for details, tap to reveal contents.
Use
Ctrl+βCtrl+βto navigate,
Ctrl+π±οΈto focus.
On Mac, use
Cmdinstead of
Ctrl.
/-
Copyright (c) 2018 Johannes HΓΆlzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes HΓΆlzl, Floris van Doorn, Mario Carneiro, Reid Barton, Johan Commelin
-/
import Mathlib.Util.Tactic
import Mathlib.Logic.Function.Basic
/-!
# `choose` tactic
Performs Skolemization, that is, given `h : β a:Ξ±, β b:Ξ², p a b |- G` produces
`f : Ξ± β Ξ², hf: β a, p a (f a) |- G`.
TODO: switch to `rcases` syntax: `choose β¨i, j, hβ -β© := expr`.
-/
open Lean Meta Elab Tactic
namespace Mathlib.Tactic.Choose
/-- Given `Ξ± : Sort u`, `nonemp : Nonempty Ξ±`, `p : Ξ± β Prop`, a context of free variables
`ctx`, and a pair of an element `val : Ξ±` and `spec : p val`,
`mk_sometimes u Ξ± nonemp p ctx (val, spec)` produces another pair `val', spec'`
such that `val'` does not have any free variables from elements of `ctx` whose types are
propositions. This is done by applying `Function.sometimes` to abstract over all the propositional
arguments. -/
def mk_sometimes (u : Level) (Ξ± nonemp p : Expr) :
List Expr β Expr Γ Expr β MetaM (Expr Γ Expr)
| [], (val, spec) => pure: {f : Type ?u.73 β Type ?u.72} β [self : Pure f] β {Ξ± : Type ?u.73} β Ξ± β f Ξ±
pure (val, spec)
| (e :: ctx), (val, spec) => do
let (val, spec) β mk_sometimes u Ξ± nonemp p ctx (val, spec)
let t β inferType e
let b β isProp t
if b then do
let val' β mkLambdaFVars #[e] val
pure: {f : Type ?u.539 β Type ?u.538} β [self : Pure f] β {Ξ± : Type ?u.539} β Ξ± β f Ξ±
pure
(mkApp4 (Expr.const ``Function.sometimes: {Ξ± : Sort u_1} β {Ξ² : Sort u_2} β [inst : Nonempty Ξ²] β (Ξ± β Ξ²) β Ξ²
Function.sometimes [Level.zero, u]) t Ξ± nonemp val',
mkApp7 (Expr.const ``Function.sometimes_spec [u]) t Ξ± nonemp p val' e spec)
else pure: {f : Type ?u.592 β Type ?u.591} β [self : Pure f] β {Ξ± : Type ?u.592} β Ξ± β f Ξ±
pure (val, spec)
/-- Results of searching for nonempty instances,
to eliminate dependencies on propositions (`choose!`).
`success` means we found at least one instance;
`failure ts` means we didn't find instances for any `t β ts`.
(`failure []` means we didn't look for instances at all.)
Rationale:
`choose!` means we are expected to succeed at least once
in eliminating dependencies on propositions.
-/
inductive ElimStatus
| success
| failure (ts : List Expr)
/-- Combine two statuses, keeping a success from either side
or merging the failures. -/
def ElimStatus.merge : ElimStatus β ElimStatus β ElimStatus
| success, _ => success
| _, success => success
| failure tsβ, failure tsβ => failure (tsβ ++ tsβ)
/-- `mkFreshNameFrom orig base` returns `mkFreshUserName base` if ``orig = `_``
and `orig` otherwise. -/
def mkFreshNameFrom (orig base : Name) : CoreM Name :=
if orig = `_ then mkFreshUserName base else pure: {f : Type ?u.3144 β Type ?u.3143} β [self : Pure f] β {Ξ± : Type ?u.3144} β Ξ± β f Ξ±
pure orig
/-- Changes `(h : βxs, βa:Ξ±, p a) β’ g` to `(d : βxs, a) β’ (s : βxs, p (d xs)) β g` and
`(h : βxs, p xs β§ q xs) β’ g` to `(d : βxs, p xs) β’ (s : βxs, q xs) β g`.
`choose1` returns a tuple of
- the error result (see `ElimStatus`)
- the data new free variable that was "chosen"
- the new goal (which contains the spec of the data as domain of an arrow type)
If `nondep` is true and `Ξ±` is inhabited, then it will remove the dependency of `d` on
all propositional assumptions in `xs`. For example if `ys` are propositions then
`(h : βxs ys, βa:Ξ±, p a) β’ g` becomes `(d : βxs, a) (s : βxs ys, p (d xs)) β’ g`. -/
def choose1 (g : MVarId) (nondep : Bool) (h : Option Expr) (data : Name) :
MetaM (ElimStatus Γ Expr Γ MVarId) := do
let (g, h) β match h with
| some e => pure: {f : Type ?u.3522 β Type ?u.3521} β [self : Pure f] β {Ξ± : Type ?u.3522} β Ξ± β f Ξ±
purepure (g, e): MetaM (?m.3466 yβ)
(gpure (g, e): MetaM (?m.3466 yβ)
, epure (g, e): MetaM (?m.3466 yβ)
)
| none => do
let (e, g) β g.intro1P
pure: {f : Type ?u.3736 β Type ?u.3735} β [self : Pure f] β {Ξ± : Type ?u.3736} β Ξ± β f Ξ±
purepure (g, .fvar e): MetaM (?m.3466 yβ)
(gpure (g, .fvar e): MetaM (?m.3466 yβ)
, .fvarpure (g, .fvar e): MetaM (?m.3466 yβ)
epure (g, .fvar e): MetaM (?m.3466 yβ)
)
g.withContext do
let h β instantiateMVars h
let t β inferType h
forallTelescopeReducing t fun ctx t β¦ do
(β withTransparency .all (whnf t)): ?m.4349
(β withTransparency(β withTransparency .all (whnf t)): ?m.4349
.all(β withTransparency .all (whnf t)): ?m.4349
(whnf(β withTransparency .all (whnf t)): ?m.4349
t(β withTransparency .all (whnf t)): ?m.4349
)).withApp fun
| .const ``Exists [u], #[Ξ±, p] => do
let data β mkFreshNameFrom data ((β p.getBinderName): ?m.4501
(β p(β p.getBinderName): ?m.4501
.getBinderName(β p.getBinderName): ?m.4501
).getD: {Ξ± : Type ?u.4551} β Option Ξ± β Ξ± β Ξ±
getD `h)
let ((neFail : ElimStatus), (nonemp : Option Expr)) β if nondep then
let ne := (Expr.const ``Nonempty [u]).app Ξ±
let m β mkFreshExprMVar ne
let mut g' := m.mvarId!
for e in ctx do
if (β isProof e) then continue
let ty β whnf (β inferType e): ?m.5334
(β inferType(β inferType e): ?m.5334
e(β inferType e): ?m.5334
)
let nety := (Expr.const ``Nonempty [u]).app ty
let neval := mkApp2 (Expr.const ``Nonempty.intro [u]) ty e
g' β g'.assert .anonymous nety neval
(_, g') β g'.intros
g'g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
.withContextg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
dog'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
matchg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
β synthInstance?g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
(β g'.getType): ?m.5855
(β g'(β g'.getType): ?m.5855
.getType(β g'.getType): ?m.5855
)g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
withg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
| someg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
eg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
=> g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
dog'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
g'g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
.assigng'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
eg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
g'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
letg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
mg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
β instantiateMVarsg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
mg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
pure: {f : Type ?u.6038 β Type ?u.6037} β [self : Pure f] β {Ξ± : Type ?u.6038} β Ξ± β f Ξ±
pureg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
(.successg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
, someg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
mg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
)
| noneg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
=> pure: {f : Type ?u.6104 β Type ?u.6103} β [self : Pure f] β {Ξ± : Type ?u.6104} β Ξ± β f Ξ±
pureg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
(.failureg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
[neg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
], noneg'.withContext do
match β synthInstance? (β g'.getType) with
| some e => do
g'.assign e
let m β instantiateMVars m
pure (.success, some m)
| none => pure (.failure [ne], none): MetaM (?m.4663 yβ)
)
else pure: {f : Type ?u.6383 β Type ?u.6382} β [self : Pure f] β {Ξ± : Type ?u.6383} β Ξ± β f Ξ±
purepure (.failure [], none): MetaM (?m.4663 yβ)
(.failurepure (.failure [], none): MetaM (?m.4663 yβ)
[]pure (.failure [], none): MetaM (?m.4663 yβ)
, nonepure (.failure [], none): MetaM (?m.4663 yβ)
)
let ctx' β if nonemp.isSome then ctxctx.filterM (not <$> isProof Β·): MetaM (?m.6526 yβ)
.filterMctx.filterM (not <$> isProof Β·): MetaM (?m.6526 yβ)
(notctx.filterM (not <$> isProof Β·): MetaM (?m.6526 yβ)
<$> isProofctx.filterM (not <$> isProof Β·): MetaM (?m.6526 yβ)
Β·) else pure: {f : Type ?u.6816 β Type ?u.6815} β [self : Pure f] β {Ξ± : Type ?u.6816} β Ξ± β f Ξ±
purepure ctx: MetaM (?m.6526 yβ)
ctx
let dataTy β mkForallFVars ctx' Ξ±
let mut dataVal := mkApp3 (.const ``Classical.choose: {Ξ± : Sort u} β {p : Ξ± β Prop} β (β x, p x) β Ξ±
Classical.choose [u]) Ξ± p (mkAppN h ctx)
let mut specVal := mkApp3 (.const ``Classical.choose_spec [u]) Ξ± p (mkAppN h ctx)
if let some nonemp := nonemp then
(dataVal, specVal) β mk_sometimes u Ξ± nonemp p ctx.toList (dataVal, specVal)
dataVal β mkLambdaFVars ctx' dataVal
specVal β mkLambdaFVars ctx specVal
let (fvar, g) β withLocalDeclD .anonymous dataTy fun d β¦ do
let specTy β mkForallFVars ctx (p.app (mkAppN d ctx')).headBeta
g.withContext <| withLocalDeclD data dataTy fun d' β¦ do
let mvarTy β mkArrow (specTy.replaceFVar d d') (β g.getType)
let newMVar β mkFreshExprSyntheticOpaqueMVar mvarTy (β g.getTag)
g.assign <| mkApp2 (β mkLambdaFVars #[d'] newMVar): ?m.8076
(β mkLambdaFVars(β mkLambdaFVars #[d'] newMVar): ?m.8076
#[d'(β mkLambdaFVars #[d'] newMVar): ?m.8076
] newMVar(β mkLambdaFVars #[d'] newMVar): ?m.8076
) dataVal specVal
pure: {f : Type ?u.8135 β Type ?u.8134} β [self : Pure f] β {Ξ± : Type ?u.8135} β Ξ± β f Ξ±
pure (d', newMVar.mvarId!)
let g β match h with
| .fvar v => gg.clear v: MetaM (?m.8349 yβ)
.clearg.clear v: MetaM (?m.8349 yβ)
v
| _ => pure: {f : Type ?u.8476 β Type ?u.8475} β [self : Pure f] β {Ξ± : Type ?u.8476} β Ξ± β f Ξ±
purepure g: MetaM (?m.8349 yβ)
g
return (neFail, fvar, g)
| .const ``And _, #[p, q] => do
let data β mkFreshNameFrom data `h
let e1 β mkLambdaFVars ctx $ mkApp3 (.const ``And.left: β {a b : Prop}, a β§ b β a
And.left []) p q (mkAppN h ctx)
let e2 β mkLambdaFVars ctx $ mkApp3 (.const ``And.right: β {a b : Prop}, a β§ b β b
And.right []) p q (mkAppN h ctx)
let t1 β inferType e1
let t2 β inferType e2
let (fvar, g) β (β (β g.assert .anonymous t2 e2).assert data t1 e1): ?m.9625
(β (β g.assert .anonymous t2 e2): ?m.9567
(β g(β g.assert .anonymous t2 e2): ?m.9567
.assert(β g.assert .anonymous t2 e2): ?m.9567
.anonymous(β g.assert .anonymous t2 e2): ?m.9567
t2(β g.assert .anonymous t2 e2): ?m.9567
e2(β g.assert .anonymous t2 e2): ?m.9567
)(β (β g.assert .anonymous t2 e2).assert data t1 e1): ?m.9625
.assert(β (β g.assert .anonymous t2 e2).assert data t1 e1): ?m.9625
data(β (β g.assert .anonymous t2 e2).assert data t1 e1): ?m.9625
t1(β (β g.assert .anonymous t2 e2).assert data t1 e1): ?m.9625
e1(β (β g.assert .anonymous t2 e2).assert data t1 e1): ?m.9625
).intro1P
let g β match h with
| .fvar v => gg.clear v: MetaM (?m.9708 yβ)
.clearg.clear v: MetaM (?m.9708 yβ)
v
| _ => pure: {f : Type ?u.9835 β Type ?u.9834} β [self : Pure f] β {Ξ± : Type ?u.9835} β Ξ± β f Ξ±
purepure g: MetaM (?m.9708 yβ)
g
return (.success, .fvar fvar, g)
-- TODO: support Ξ£, Γ, or even any inductive type with 1 constructor ?
| _, _ => throwError "expected a term of the shape `βxs, βa, p xs a` or `βxs, p xs β§ q xs`": ?m.10310 ?m.10311
throwErrorthrowError "expected a term of the shape `βxs, βa, p xs a` or `βxs, p xs β§ q xs`": ?m.10310 ?m.10311
"expected a term of the shape `βxs, βa, p xs a` or `βxs, p xs β§ q xs`"
/-- A wrapper around `choose1` that parses identifiers and adds variable info to new variables. -/
def choose1WithInfo (g : MVarId) (nondep : Bool) (h : Option Expr) (data : TSyntax ``binderIdent) :
TermElabM (ElimStatus Γ MVarId) := do
let n := if let `(binderIdent| $n:ident) := data then n.getId else `_
let (status, fvar, g) β choose1 g nondep h n
g.withContext <| fvar.addLocalVarInfoForBinderIdent data
pure: {f : Type ?u.32634 β Type ?u.32633} β [self : Pure f] β {Ξ± : Type ?u.32634} β Ξ± β f Ξ±
pure (status, g)
/-- A loop around `choose1`. The main entry point for the `choose` tactic. -/
def elabChoose (nondep : Bool) (h : Option Expr) :
List (TSyntax ``binderIdent) β ElimStatus β MVarId β TermElabM MVarId
| [], _, _ => throwError "expect list of variables": ?m.33981 ?m.33982
throwErrorthrowError "expect list of variables": ?m.33981 ?m.33982
"expect list of variables"
| [n], status, g =>
match nondep, status with
| true, .failure tys => do -- We expected some elimination, but it didn't happen.
let mut msg := m!"choose!: failed to synthesize any nonempty instances": MessageData
m!m!"choose!: failed to synthesize any nonempty instances": MessageData
"choose!: failed to synthesize any nonempty instances"
for ty in tys do
msg := msg ++ m!"{(β mkFreshExprMVar ty): ?m.34633
(β mkFreshExprMVar(β mkFreshExprMVar ty): ?m.34633
ty(β mkFreshExprMVar ty): ?m.34633
).mvarId!}"
throwError msg
| _, _ => do
let (fvar, g) β match n with
| `(binderIdent| $n:ident) => g.intro n.getId
| _ => g.intro1
g.withContext <| (Expr.fvar fvar).addLocalVarInfoForBinderIdent n
return g
| n::ns, status, g => do
let (status', g) β choose1WithInfo g nondep h n
elabChoose nondep none ns (status.merge status') g
/--
* `choose a b h h' using hyp` takes a hypothesis `hyp` of the form
`β (x : X) (y : Y), β (a : A) (b : B), P x y a b β§ Q x y a b`
for some `P Q : X β Y β A β B β Prop` and outputs
into context a function `a : X β Y β A`, `b : X β Y β B` and two assumptions:
`h : β (x : X) (y : Y), P x y (a x y) (b x y)` and
`h' : β (x : X) (y : Y), Q x y (a x y) (b x y)`. It also works with dependent versions.
* `choose! a b h h' using hyp` does the same, except that it will remove dependency of
the functions on propositional arguments if possible. For example if `Y` is a proposition
and `A` and `B` are nonempty in the above example then we will instead get
`a : X β A`, `b : X β B`, and the assumptions
`h : β (x : X) (y : Y), P x y (a x) (b x)` and
`h' : β (x : X) (y : Y), Q x y (a x) (b x)`.
The `using hyp` part can be ommited,
which will effectively cause `choose` to start with an `intro hyp`.
Examples:
```
example (h : β n m : β, β i j, m = n + i β¨ m + j = n) : True := by
choose i j h using h
guard_hyp i : β β β β β
guard_hyp j : β β β β β
guard_hyp h : β (n m : β), m = n + i n m β¨ m + j n m = n
trivial
```
```
example (h : β i : β, i < 7 β β j, i < j β§ j < i+i) : True := by
choose! f h h' using h
guard_hyp f : β β β
guard_hyp h : β (i : β), i < 7 β i < f i
guard_hyp h' : β (i : β), i < 7 β f i < i + i
trivial
```
-/
syntax (name := choose) "choose" "!"? (colGt binderIdent)+ (" using " term)? : tactic
elab_rules : tactic
| `(tactic| choose $[!%$b]? $[$ids]* $[using $h]?) => withMainContext do
let h β h.mapM (Elab.Tactic.elabTerm Β· none)
let g β elabChoose b.isSome h ids.toList (.failure []) (β getMainGoal): ?m.41322
(β getMainGoal(β getMainGoal): ?m.41322
)
replaceMainGoal [g]
@[inherit_doc choose]
syntax "choose!" (colGt binderIdent)+ (" using " term)? : tactic
macro_rules
| `(tactic| choose! $[$ids]* $[using $h]?) => `(tactic| choose ! $[$ids]* $[using $h]?)