Zulip Chat Archive
Stream: lean4
Topic: Telescope action on binders/let values
Arthur Adjedj (Jan 08 2024 at 15:01):
Hi. Currently, functions like forallTelescope and lambdaTelescope are useful in that they allow one to traverse terms easily, but don't allow the user to i.e influence the values/types of variables added in the local context. 
Take for example the following function in Lean.Meta.Reduce, which is used to elaborate the #reduce command:
partial def reduce (e : Expr) (explicitOnly skipTypes skipProofs := true) : MetaM Expr :=
  let rec visit (e : Expr) : MonadCacheT Expr Expr MetaM Expr :=
    checkCache e fun _ => Core.withIncRecDepth do
      if (← (pure skipTypes <&&> isType e)) then
        return e
      else if (← (pure skipProofs <&&> isProof e)) then
        return e
      else
        let e ← whnf e
        match e with
        | Expr.app .. =>
          let f     ← visit e.getAppFn
          let nargs := e.getAppNumArgs
          let finfo ← getFunInfoNArgs f nargs
          let mut args  := e.getAppArgs
          for i in [:args.size] do
            if i < finfo.paramInfo.size then
              let info := finfo.paramInfo[i]!
              if !explicitOnly || info.isExplicit then
                args ← args.modifyM i visit
            else
              args ← args.modifyM i visit
          if f.isConstOf ``Nat.succ && args.size == 1 && args[0]!.isNatLit then
            return mkRawNatLit (args[0]!.natLit?.get! + 1)
          else
            return mkAppN f args
        | Expr.lam ..        => lambdaTelescope e fun xs b => do mkLambdaFVars xs (← visit b)
        | Expr.forallE ..    => forallTelescope e fun xs b => do mkForallFVars xs (← visit b)
        | Expr.proj n i s .. => return mkProj n i (← visit s)
        | _                  => return e
  visit e |>.run
This function visits and reduces every redex. However, it doesn't reduce binder types. For example, #reduce (x : id Nat) -> Fin x doesn't reduce id Nat. My initial attempt at fixing such things has been to manipulate the local context during the traversal, by overwriting variables to the local context and modifying their types/values, as such:
forallTelescope e fun xs b => do
  let mut lctx ← getLCtx
  for e in xs do
    let some lcdl := lctx.findFVar? e | unreachable!
    let value ← withReader (fun ctx => {ctx with lctx := lctx})
      (visit lcdl.value)
    lctx := LocalContext.addDecl lctx (lcdl.setValue value)
  withReader (fun ctx => {ctx with lctx := lctx})
    (mkForallFVars xs (← visit b))
but this doesn't look elegant nor efficient to me. Is there any nice way to do something like this, apart from writing separate telescope functions ?
Arthur Adjedj (Jan 08 2024 at 15:06):
This is one example where I've encountered the need to do such a thing, a second one, which occurred to me now, happens here: the let declarations' bodies don't get reduced before being added to the local context, leading to some metavariables not getting assigned. Fixing this resolves #3146
Last updated: May 02 2025 at 03:31 UTC