Zulip Chat Archive

Stream: general

Topic: proof mode: split out tactics into separate commands


Mario Carneiro (Aug 12 2023 at 17:52):

Mario Carneiro said:

But if lean had a proof mode like HOL light's e(...) steps broken out as separate lean commands then you would certainly get back the speed benefits you are seeing in HOL Light

Mario Carneiro said:

now I think I want to try to demo something like that in lean

Here's what I was talking about @D. J. Bernstein : This is a prototype implementation of a "proof mode" in which you can save the context of a proof in a by block by writing start_proof, and then you can start writing tactics in separate commands thereafter using ## before each tactic (or group of tactics). Because lean automatically snapshots between commands, you get the benefits of a line-by-line proof like in Coq or HOL light in terms of general responsiveness. This may be considered as an alternative to the save tactic, which tries to do something similar but without linearizing the AST structure.

Mario Carneiro (Aug 12 2023 at 17:53):

Mathlib.Tactic.ProofMode:

import Lean
open Lean

structure ProofState where
  tacCtx : Elab.Tactic.Context
  tacState : Elab.Tactic.State
  termCtx : Elab.Term.Context
  termState : Elab.Term.State
  metaCtx : Meta.Context
  metaState : Meta.State

def ProofState.get : Elab.Tactic.TacticM ProofState :=
  fun tacCtx tacState termCtx termState metaCtx metaState => do
    let tacState  tacState.get
    let termState  termState.get
    let metaState  metaState.get
    return { tacCtx, tacState, termCtx, termState, metaCtx, metaState }

def ProofState.with : ProofState  Elab.Tactic.TacticM α  CoreM α :=
  fun { tacCtx, tacState, termCtx, termState, metaCtx, metaState } x => do
    let tacState  IO.mkRef tacState
    let termState  IO.mkRef termState
    let metaState  IO.mkRef metaState
    x tacCtx tacState termCtx termState metaCtx metaState

initialize proofStateExt : EnvExtension (List ProofState)  registerEnvExtension (pure [])

elab "start_proof" : tactic => do
  let state  ProofState.get
  modifyEnv (proofStateExt.modifyState · (state :: ·))
  Elab.Tactic.evalTactic ( `(tactic| repeat sorry))

elab tk:"##" tac:(tacticSeq)? : command => do
  let state :: rest := proofStateExt.getState ( getEnv)
    | throwError "proof mode not started, use `start_proof`"
  Elab.Command.liftTermElabM do
    state.with do
      Elab.Tactic.withTacticInfoContext tk do
        if let some tac := tac then
          Elab.Tactic.evalTactic tac
      if ( Elab.Tactic.getUnsolvedGoals).isEmpty then
        logInfoAt tk "Goals accomplished 🎉"
        modifyEnv (proofStateExt.setState · rest)
      else
        modifyEnv (proofStateExt.setState · (( ProofState.get) :: rest))

Mario Carneiro (Aug 12 2023 at 17:54):

test file:

import Mathlib.Tactic.ProofMode

theorem T :  x, x < x + 1 := by
  constructor
  start_proof -- this pushes the current proof state (note, it doesn't work in `example`)

## -- cursor here to see proof state
## refine Nat.lt_succ_self ?_ -- you can write a tactic or tactic block here
## exact 1 -- when the goal is finished, it pops the proof state and reports "Goals accomplished 🎉"

Mario Carneiro (Aug 12 2023 at 19:02):

It would be nice to get some experience reports with this from people who like to write long tactic proofs and struggle to keep things responsive

D. J. Bernstein (Aug 12 2023 at 19:17):

Will try with my next Lean project, thanks!

Bolton Bailey (Jan 12 2024 at 12:32):

Mario Carneiro said:

It would be nice to get some experience reports with this from people who like to write long tactic proofs and struggle to keep things responsive

I just tried it on my long-running crypto proof (~35 tactic calls, most of the long ones are simp only calls on a bunch of hypotheses, runs in about 200 seconds on my laptop)

  • Tactic processing restarts from the point of my last edit, not the beginning :+1:
  • I can see about how long each tactic call is taking without any other kind of profiling, frankly this alone makes it a useful tool.
  • Works on the tactics defined in my project as well as standard mathlib tactics

I am not being able to see the actual proof state, it perpetually says "loading messages". (This was already a problem for this codebase - it was taking a long time to update the info view as I moved my cursor around, but now it just doesn't show things at all). Still, tactics like polyrith I can see the output by mousing over, so it's clear the tactics are being run.

Mario Carneiro (Jan 12 2024 at 16:32):

@Bolton Bailey Can you make a MWE for the loading issue? Or a project I can pull

Bolton Bailey (Jan 12 2024 at 16:38):

After VSCode froze and I rebooted it, I'm not actually getting the perpetual loading problem anymore.

Bolton Bailey (Jan 12 2024 at 16:42):

It's still slow to change the infoview when moving the cursor, but that was the case before I added start_proof. I haven't put up the code yet, I was planning to just replace my lean3 repository with my lean 4 code when it's ported.

Bolton Bailey (Jan 12 2024 at 16:52):

Ok the code is now here the file I am working on is FormalSnarksProject.SNARKs.Groth16TypeIII

Eric Rodriguez (Feb 01 2024 at 14:16):

I just found this by coincidence - this seems amazing! Mario, have you further worked on this?

Patrick Massot (Feb 01 2024 at 14:53):

@Sebastian Ullrich is preparing the ultimate solution to this issue. See his talk at the Lean Together conference a couple of weeks ago.

Mario Carneiro (Feb 02 2024 at 07:13):

I think it might be good to have it in mathlib anyway, this has been on the roadmap for ages

Mario Carneiro (Feb 02 2024 at 07:15):

and to answer your question @Eric Rodriguez , no I haven't done anything else with this but it's usable as is. It's mainly intended for temporary use while working on long meandering proofs that have not yet been put in mathlib style


Last updated: May 02 2025 at 03:31 UTC