Zulip Chat Archive

Stream: Is there code for X?

Topic: Transforming an expr when moving into the alts of a case


Joachim Breitner (Nov 09 2023 at 17:19):

Meta-programming question:

I am traversing an expression (a function definition), and I also have an e : Expr of interest at hand. As I go into the alts of a casesOn combinator, if that case analysis looks at e, I want to get the “refined” expression.

For example, say e is (a, b), and I enter PSigma.casesOn b (fun c d = …), then I want to know that instead of (a, b) I now have (a (c, d)).

There is something very close to that in Lean.Meta.CasesOnApp.addArg, which I can use if e is a type and I have an actual value v : e around that I can thread though, and as part of that process, that function does infer the “refined” type of v. But what do I do if I don’t have a value, and just the expression e that I want to transform?

(I tried cargo-culting addArg, but it doesn’t seem to work right.)

Joachim Breitner (Nov 09 2023 at 17:50):

This seem to be roughly doing what I think I need, but I have serious doubts that this is idiomatic good style…

/--
  Given a `casesOn` application `c` of the form
  ```
  casesOn As (fun is x => motive[i, xs]) is major  (fun ys_1 => (alt_1 : motive (C_1[ys_1])) ... (fun ys_n => (alt_n : motive (C_n[ys_n]) remaining
  ```
  and a type `e : B[is, major]`, for every alternative `i`, construct the type
  ```
  B[C_i[ys_i]]
  ```
  (which `ys_i` as loose bound variable, ready to be `.instantiateRev`d)
-/
def _root_.Lean.Meta.CasesOnApp.transform (c : CasesOnApp) (e : Expr) : MetaM (Array Expr) :=
  lambdaTelescope c.motive fun motiveArgs _motiveBody => do
    unless motiveArgs.size == c.indices.size + 1 do
      throwError "failed to add argument to `casesOn` application, motive must be lambda expression with #{c.indices.size + 1} binders"
    let discrs := c.indices ++ #[c.major]
    let mut eAbst := e
    for motiveArg in motiveArgs.reverse, discr in discrs.reverse do
      eAbst := ( kabstract eAbst discr).instantiate1 motiveArg
    -- Up to this point, this is cargo-culted from `CasesOn.App.addArg`
    -- Let's create something Prop-typed that mentions `e`, by writing `e = e`.
    let eEq  mkEq eAbst eAbst
    let motive  mkLambdaFVars motiveArgs eEq
    let us := if c.propOnly then c.us else levelZero :: c.us.tail!
    -- Now instantiate the casesOn wth this synthetic motive
    let aux := mkAppN (mkConst c.declName us) c.params
    let aux := mkApp aux motive
    let aux := mkAppN aux discrs
    check aux
    let auxType  inferType aux
    -- The type of the remaining arguments will mention `e` instantiated for each arg
    -- so extract them
    let (altAuxs, _, _)  Lean.Meta.forallMetaTelescope auxType
    let altAuxTys  altAuxs.mapM inferType
    let res  altAuxTys.mapM fun altAux => do
      let (fvs, _, body)  Lean.Meta.forallMetaTelescope altAux
      let body := body.getArg! 2
      -- and abstract over the parameters of the alternative again
      Expr.abstractM body fvs
    return res

Scott Morrison (Nov 11 2023 at 03:42):

Besides the repeated let aux := it doesn't seem crazy.

Joachim Breitner (Nov 11 2023 at 10:23):

Ha, and that's something I have carco-culted from the lean code:
https://github.com/leanprover/lean4/blob/baa4b68a71926a722b77d4ec2cba34bfc76cc5aa/src/Lean/Meta/CasesOn.lean#L74


Last updated: Dec 20 2023 at 11:08 UTC