lean4-htt/src/Init/Try.lean
Kim Morrison 5bb7f37645
feat: add first_par combinator for try? with grind +locals (#11949)
This PR adds a new `first_par` tactic combinator that runs multiple
tactics in parallel and returns the first successful result (cancelling
the others).

The `try?` tactic's `atomicSuggestions` step now uses `first_par` to try
three grind variants in parallel:
- `grind? +suggestions` - uses library suggestion engine  
- `grind? +locals` - unfolds local definitions from current file
- `grind? +locals +suggestions` - combines both

This leverages `TacticM.parFirst` which already provides the "first
success wins" parallel execution with cancellation.

### Depends on
- [x] depends on: #11946

🤖 Prepared with Claude Code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-09 21:09:41 +00:00

102 lines
3.4 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
module
prelude
public import Init.Tactics
public section
namespace Lean.Try
/--
Configuration for `try?`.
-/
structure Config where
/-- If `main` is `true`, all functions in the current module are considered for function induction, unfolding, etc. -/
main := true
/-- If `name` is `true`, all functions in the same namespace are considered for function induction, unfolding, etc. -/
name := true
/-- If `targetOnly` is `true`, `try?` collects information using the goal target only. -/
targetOnly := false
/-- Maximum number of suggestions. -/
max := 8
/-- If `missing` is `true`, allows the construction of partial solutions where some of the subgoals are `sorry`. -/
missing := false
/-- If `only` is `true`, generates solutions using `grind only` and `simp only`. -/
only := true
/-- If `harder` is `true`, more expensive tactics and operations are tried. -/
harder := false
/--
If `merge` is `true`, it tries to compress suggestions such as
```
induction a
· grind only [= f]
· grind only [→ g]
```
as
```
induction a <;> grind only [= f, → g]
```
-/
merge := true
/-- If `wrapWithBy` is `true`, suggestions are wrapped with `by` for term mode usage. -/
wrapWithBy := false
deriving Inhabited
end Lean.Try
namespace Lean.Parser.Tactic
syntax (name := tryTrace) "try?" optConfig : tactic
/-- Helper internal tactic for implementing the tactic `try?`. -/
syntax (name := attemptAll) "attempt_all " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic for implementing the tactic `try?` with parallel execution. -/
syntax (name := attemptAllPar) "attempt_all_par " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic for implementing the tactic `try?` with parallel execution, returning first success. -/
syntax (name := firstPar) "first_par " withPosition((ppDedent(ppLine) colGe "| " tacticSeq)+) : tactic
/-- Helper internal tactic used to implement `evalSuggest` in `try?` -/
syntax (name := tryResult) "try_suggestions " tactic* : tactic
end Lean.Parser.Tactic
namespace Lean.Parser.Command
/--
`register_try?_tactic tac` registers a tactic `tac` as a suggestion generator for the `try?` tactic.
An optional priority can be specified with `register_try?_tactic (priority := 500) tac`.
Higher priority generators are tried first. The default priority is 1000.
-/
syntax (name := registerTryTactic) (docComment)?
"register_try?_tactic" ("(" &"priority" ":=" num ")")? tacticSeq : command
end Lean.Parser.Command
/-- `∎` (typed as `\qed`) is a macro that expands to `try?` in tactic mode. -/
macro "∎" : tactic => `(tactic| try?)
/-- `∎` (typed as `\qed`) is a macro that expands to `by try? (wrapWithBy := true)` in term mode.
The `wrapWithBy` config option causes suggestions to be prefixed with `by`. -/
macro "∎" : term => `(by try? (wrapWithBy := true))
namespace Lean.Try
/--
Marker for try?-solved subproofs in `exact? +try?` suggestions.
When `exact?` uses try? as a discharger, it wraps the proof in this marker
so that the unexpander can replace it with `(by try?)` in the suggestion.
-/
@[inline] def Marker {α : Sort u} (a : α) : α := a
@[app_unexpander Marker]
meta def markerUnexpander : PrettyPrinter.Unexpander := fun _ => do
`(by try?)
end Lean.Try