Skip to content

Commit

Permalink
refactor: Introduce PProdN module (#4807)
Browse files Browse the repository at this point in the history
code to create nested `PProd`s, and project out, and related functions
were scattered in variuos places. This unifies them in
`Lean.Meta.PProdN`.

It also consistently avoids the terminal `True` or `PUnit`, for slightly
easier to read constructions.
  • Loading branch information
nomeata authored Jul 22, 2024
1 parent 22ae04f commit 3a4d2cd
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 286 deletions.
19 changes: 9 additions & 10 deletions src/Lean/Elab/PreDefinition/Structural/BRecOn.lean
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ Authors: Leonardo de Moura, Joachim Breitner
-/
prelude
import Lean.Util.HasConstCache
import Lean.Meta.PProdN
import Lean.Meta.Match.MatcherApp.Transform
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Elab.PreDefinition.Structural.FunPacker
import Lean.Elab.PreDefinition.Structural.RecArgInfo

namespace Lean.Elab.Structural
Expand All @@ -21,11 +21,11 @@ private def throwToBelowFailed : MetaM α :=
partial def searchPProd (e : Expr) (F : Expr) (k : Expr → Expr → MetaM α) : MetaM α := do
match (← whnf e) with
| .app (.app (.const `PProd _) d1) d2 =>
(do searchPProd d1 (← mkAppM ``PProd.fst #[F]) k)
<|> (do searchPProd d2 (← mkAppM `PProd.snd #[F]) k)
(do searchPProd d1 (.proj ``PProd 0 F) k)
<|> (do searchPProd d2 (.proj ``PProd 1 F) k)
| .app (.app (.const `And _) d1) d2 =>
(do searchPProd d1 (← mkAppM `And.left #[F]) k)
<|> (do searchPProd d2 (← mkAppM `And.right #[F]) k)
(do searchPProd d1 (.proj `And 0 F) k)
<|> (do searchPProd d2 (.proj `And 1 F) k)
| .const `PUnit _
| .const `True _ => throwToBelowFailed
| _ => k e F
Expand Down Expand Up @@ -85,7 +85,7 @@ private def withBelowDict [Inhabited α] (below : Expr) (numIndParams : Nat)
return ((← mkFreshUserName `C), fun _ => pure t)
withLocalDeclsD CDecls fun Cs => do
-- We have to pack these canary motives like we packed the real motives
let packedCs ← positions.mapMwith packMotives motiveTypes Cs
let packedCs ← positions.mapMwith PProdN.packLambdas motiveTypes Cs
let belowDict := mkAppN pre packedCs
let belowDict := mkAppN belowDict finalArgs
trace[Elab.definition.structural] "initial belowDict for {Cs}:{indentExpr belowDict}"
Expand Down Expand Up @@ -250,7 +250,7 @@ def mkBRecOnConst (recArgInfos : Array RecArgInfo) (positions : Positions)
let brecOnAux := brecOnCons 0
-- Infer the type of the packed motive arguments
let packedMotiveTypes ← inferArgumentTypesN indGroup.numMotives brecOnAux
let packedMotives ← positions.mapMwith packMotives packedMotiveTypes motives
let packedMotives ← positions.mapMwith PProdN.packLambdas packedMotiveTypes motives

return fun n => mkAppN (brecOnCons n) packedMotives

Expand Down Expand Up @@ -289,12 +289,11 @@ def mkBrecOnApp (positions : Positions) (fnIdx : Nat) (brecOnConst : Nat → Exp
let brecOn := brecOnConst recArgInfo.indIdx
let brecOn := mkAppN brecOn indexMajorArgs
let packedFTypes ← inferArgumentTypesN positions.size brecOn
let packedFArgs ← positions.mapMwith packFArgs packedFTypes FArgs
let packedFArgs ← positions.mapMwith PProdN.mkLambdas packedFTypes FArgs
let brecOn := mkAppN brecOn packedFArgs
let some poss := positions.find? (·.contains fnIdx)
| throwError "mkBrecOnApp: Could not find {fnIdx} in {positions}"
let brecOn ← if poss.size = 1 then pure brecOn else
mkPProdProjN (poss.getIdx? fnIdx).get! brecOn
let brecOn ← PProdN.proj poss.size (poss.getIdx? fnIdx).get! brecOn
mkLambdaFVars ys (mkAppN brecOn otherArgs)

end Lean.Elab.Structural
126 changes: 0 additions & 126 deletions src/Lean/Elab/PreDefinition/Structural/FunPacker.lean

This file was deleted.

21 changes: 0 additions & 21 deletions src/Lean/Meta/AppBuilder.lean
Original file line number Diff line number Diff line change
Expand Up @@ -665,27 +665,6 @@ def mkIffOfEq (h : Expr) : MetaM Expr := do
else
mkAppM ``Iff.of_eq #[h]

/--
Given proofs of `P₁`, …, `Pₙ`, returns a proof of `P₁ ∧ … ∧ Pₙ`.
If `n = 0` returns a proof of `True`.
If `n = 1` returns the proof of `P₁`.
-/
def mkAndIntroN : Array Expr → MetaM Expr
| #[] => return mkConst ``True.intro []
| #[e] => return e
| es => es.foldrM (start := es.size - 1) (fun a b => mkAppM ``And.intro #[a,b]) es.back


/-- Given a proof of `P₁ ∧ … ∧ Pᵢ ∧ … ∧ Pₙ`, return the proof of `Pᵢ` -/
def mkProjAndN (n i : Nat) (e : Expr) : Expr := Id.run do
let mut value := e
for _ in [:i] do
value := mkProj ``And 1 value
if i + 1 < n then
value := mkProj ``And 0 value
return value


builtin_initialize do
registerTraceClass `Meta.appBuilder
registerTraceClass `Meta.appBuilder.result (inherited := true)
Expand Down
5 changes: 3 additions & 2 deletions src/Lean/Meta/ArgsPacker.lean
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Authors: Joachim Breitner

prelude
import Lean.Meta.AppBuilder
import Lean.Meta.PProdN
import Lean.Meta.ArgsPacker.Basic

/-!
Expand Down Expand Up @@ -518,7 +519,7 @@ def curry (argsPacker : ArgsPacker) (e : Expr) : MetaM Expr := do
let mut es := #[]
for i in [:argsPacker.numFuncs] do
es := es.push (← argsPacker.curryProj e i)
mkAndIntroN es
PProdN.mk 0 es

/--
Given type `(a ⊗' b ⊕' c ⊗' d) → e`, brings `a → b → e` and `c → d → e`
Expand All @@ -533,7 +534,7 @@ where
| [], acc => k acc
| t::ts, acc => do
let name := if argsPacker.numFuncs = 1 then name else .mkSimple s!"{name}{acc.size+1}"
withLocalDecl name .default t fun x => do
withLocalDeclD name t fun x => do
go ts (acc.push x)

/--
Expand Down
79 changes: 12 additions & 67 deletions src/Lean/Meta/Constructions/BRecOn.lean
Original file line number Diff line number Diff line change
Expand Up @@ -8,67 +8,18 @@ import Lean.Meta.InferType
import Lean.AuxRecursor
import Lean.AddDecl
import Lean.Meta.CompletionName
import Lean.Meta.PProdN

namespace Lean
open Meta

section PProd

/--!
Helpers to construct types and values of `PProd` and project out of them, set up to use `And`
instead of `PProd` if the universes allow. Maybe be extracted into a Utils module when useful
elsewhere.
-/

private def mkPUnit : Level → Expr
| .zero => .const ``True []
| lvl => .const ``PUnit [lvl]

private def mkPProd (e1 e2 : Expr) : MetaM Expr := do
let lvl1 ← getLevel e1
let lvl2 ← getLevel e2
if lvl1 matches .zero && lvl2 matches .zero then
return mkApp2 (.const `And []) e1 e2
else
return mkApp2 (.const ``PProd [lvl1, lvl2]) e1 e2

private def mkNProd (lvl : Level) (es : Array Expr) : MetaM Expr :=
es.foldrM (init := mkPUnit lvl) mkPProd

private def mkPUnitMk : Level → Expr
| .zero => .const ``True.intro []
| lvl => .const ``PUnit.unit [lvl]

private def mkPProdMk (e1 e2 : Expr) : MetaM Expr := do
let t1 ← inferType e1
let t2 ← inferType e2
let lvl1 ← getLevel t1
let lvl2 ← getLevel t2
if lvl1 matches .zero && lvl2 matches .zero then
return mkApp4 (.const ``And.intro []) t1 t2 e1 e2
else
return mkApp4 (.const ``PProd.mk [lvl1, lvl2]) t1 t2 e1 e2

private def mkNProdMk (lvl : Level) (es : Array Expr) : MetaM Expr :=
es.foldrM (init := mkPUnitMk lvl) mkPProdMk

/-- `PProd.fst` or `And.left` (as projections) -/
private def mkPProdFst (e : Expr) : MetaM Expr := do
let t ← whnf (← inferType e)
match_expr t with
| PProd _ _ => return .proj ``PProd 0 e
| And _ _ => return .proj ``And 0 e
| _ => throwError "Cannot project .1 out of{indentExpr e}\nof type{indentExpr t}"

/-- `PProd.snd` or `And.right` (as projections) -/
private def mkPProdSnd (e : Expr) : MetaM Expr := do
let t ← whnf (← inferType e)
match_expr t with
| PProd _ _ => return .proj ``PProd 1 e
| And _ _ => return .proj ``And 1 e
| _ => throwError "Cannot project .2 out of{indentExpr e}\nof type{indentExpr t}"

end PProd
/-- Transforms `e : xᵢ → (t₁ ×' t₂)` into `(xᵢ → t₁) ×' (xᵢ → t₂) -/
private def etaPProd (xs : Array Expr) (e : Expr) : MetaM Expr := do
if xs.isEmpty then return e
let r := mkAppN e xs
let r₁ ← mkLambdaFVars xs (← mkPProdFst r)
let r₂ ← mkLambdaFVars xs (← mkPProdSnd r)
mkPProdMk r₁ r₂

/--
If `minorType` is the type of a minor premies of a recursor, such as
Expand All @@ -91,7 +42,7 @@ private def buildBelowMinorPremise (rlvl : Level) (motives : Array Expr) (minorT
where
ibelow := rlvl matches .zero
go (prods : Array Expr) : List Expr → MetaM Expr
| [] => mkNProd rlvl prods
| [] => PProdN.pack rlvl prods
| arg::args => do
let argType ← inferType arg
forallTelescope argType fun arg_args arg_type => do
Expand Down Expand Up @@ -243,7 +194,7 @@ private def buildBRecOnMinorPremise (rlvl : Level) (motives : Array Expr)
forallTelescope minorType fun minor_args minor_type => do
let rec go (prods : Array Expr) : List Expr → MetaM Expr
| [] => minor_type.withApp fun minor_type_fn minor_type_args => do
let b ← mkNProdMk rlvl prods
let b ← PProdN.mk rlvl prods
let .some ⟨idx, _⟩ := motives.indexOf? minor_type_fn
| throwError m!"Did not find {minor_type} in {motives}"
mkPProdMk (mkAppN fs[idx]! (minor_type_args.push b)) b
Expand All @@ -256,14 +207,8 @@ private def buildBRecOnMinorPremise (rlvl : Level) (motives : Array Expr)
let type' ← mkForallFVars arg_args
(← mkPProd arg_type (mkAppN belows[idx]! arg_type_args) )
withLocalDeclD name type' fun arg' => do
if arg_args.isEmpty then
mkLambdaFVars #[arg'] (← go (prods.push arg') args)
else
let r := mkAppN arg' arg_args
let r₁ ← mkLambdaFVars arg_args (← mkPProdFst r)
let r₂ ← mkLambdaFVars arg_args (← mkPProdSnd r)
let r ← mkPProdMk r₁ r₂
mkLambdaFVars #[arg'] (← go (prods.push r) args)
let r ← etaPProd arg_args arg'
mkLambdaFVars #[arg'] (← go (prods.push r) args)
else
mkLambdaFVars #[arg] (← go prods args)
go #[] minor_args.toList
Expand Down
Loading

0 comments on commit 3a4d2cd

Please sign in to comment.