Zulip Chat Archive
Stream: new members
Topic: How to learn to write tactics?
Kenny Lau (Aug 07 2025 at 11:49):
There's a tactic that I want to write, and I'm starting off with basically zero knowledge of tactics, and I've been following the following algorithm:
- Find an existing tactic with similar behaviour
- Copy the source file to Lean 4 Web
- Try to follow every step of the tactic (putting it in the web editor helps me to click around)
Is there a more efficient algorithm that I can follow? (I suppose I could open it in VSCode as well, but beyond that?)
Arthur Paulino (Aug 07 2025 at 12:09):
I think https://leanprover-community.github.io/lean4-metaprogramming-book/ would improve your skills to read and understand existing tactics
Arthur Paulino (Aug 07 2025 at 12:10):
<deleted>
Chris Bailey (Aug 07 2025 at 13:33):
You can leverage your social connections here and get someone who knows more about writing tactics to help.
Kevin Buzzard (Aug 08 2025 at 15:08):
Heather's course https://github.com/hrmacbeth/metaprogramming was interesting but it wasn't designed for remote learning, "you had to be there" basically.
Kevin Buzzard (Aug 08 2025 at 15:08):
did she give lectures about this stuff at Simons and are they videoed?
Patrick Massot (Aug 08 2025 at 22:18):
Kenny, see also https://github.com/mirefek/lean-tactic-programming-guide
Kenny Lau (Aug 08 2025 at 22:19):
Thanks!
Kenny Lau (Aug 08 2025 at 22:20):
I would like to announce that I successfully wrote my first tactic in #28072 :tada:
Kenny Lau (Aug 08 2025 at 22:20):
(golfing/general advice is very welcome)
Kenny Lau (Aug 09 2025 at 06:51):
import Mathlib
open Lean
initialize registerTraceClass `rw_equiv_tac
set_option trace.rw_equiv_tac true
-- unknown option 'trace.rw_equiv_tac'
Kenny Lau (Aug 09 2025 at 06:51):
Does register only work in the next file?
Kenny Lau (Aug 09 2025 at 16:21):
so i've been told that Qq is very hard to debug
Kenny Lau (Aug 09 2025 at 16:21):
it's complaining about a type mismatch and i don't know how to fix it
Aaron Liu (Aug 09 2025 at 16:22):
#mwe?
Kenny Lau (Aug 09 2025 at 16:22):
also, the expected type window doesn't work very well for me, it seems to be always stuck on the previous state
Kenny Lau (Aug 09 2025 at 16:22):
import Mathlib
open Lean Meta Tactic Simp Qq
simproc matrix_diagonal (Matrix.diagonal _) := .ofQ fun u α M? ↦ do
let .succ u := u | return .continue
let ~q(@Matrix (Fin (OfNat.ofNat $k?)) (Fin (OfNat.ofNat $k?)) $R) := α | return .continue
let ~q(@Matrix.diagonal.{u, 0} (Fin (OfNat.ofNat $k?)) $R $dF $zR $v) := M? | return .continue
return .continue
Aaron Liu (Aug 09 2025 at 16:23):
yeah Qq is sort of broken sometimes
Kenny Lau (Aug 09 2025 at 16:23):
error:
type mismatch
Matrix.diagonal ?m✝
has type
Matrix (Fin (OfNat.ofNat ?m✝)) (Fin (OfNat.ofNat ?m✝)) ?m✝¹ : Type u
but is expected to have type
«$α» : Type u
Aaron Liu (Aug 09 2025 at 16:28):
oh I fixed it
Aaron Liu (Aug 09 2025 at 16:28):
import Mathlib
open Lean Meta Tactic Simp Qq
simproc matrix_diagonal (Matrix.diagonal _) := .ofQ fun u α M? ↦ do
let .succ u := u | return .continue
let ~q(@Matrix (Fin (OfNat.ofNat $k?)) (Fin (OfNat.ofNat $k?)) $R) := α | return .continue
let ~q(@Matrix.diagonal.{u, 0} (Fin (OfNat.ofNat _)) _ $dF $zR $v) := M? | return .continue
return .continue
Kenny Lau (Aug 09 2025 at 16:29):
do you know how to read the correct expected type?
Kenny Lau (Aug 09 2025 at 16:31):
oh... i have to do the french quotes? lol
Aaron Liu (Aug 09 2025 at 16:31):
probably not?
Aaron Liu (Aug 09 2025 at 16:31):
I was just trying things until it worked
Kenny Lau (Aug 09 2025 at 16:31):
i just compared your code with mine and only saw the french quote as the difference
Aaron Liu (Aug 09 2025 at 16:32):
yeah it works without them too (edited)
Kenny Lau (Aug 09 2025 at 16:33):
well, ok, so it works with _ or with french quotes «$k?», just not with $k?
Aaron Liu (Aug 09 2025 at 16:34):
yeah
Aaron Liu (Aug 09 2025 at 16:34):
I think it's because just $k? will make it a pattern variable
Kenny Lau (Aug 09 2025 at 20:24):
Kenny Lau (Aug 09 2025 at 20:24):
Is there a Qq version of taking the "forall closure"?
Aaron Liu (Aug 09 2025 at 20:24):
I don't think you can use Qq to do that
Aaron Liu (Aug 09 2025 at 20:25):
since it doesn't store the context your Exprs are supposed to be typed in
Aaron Liu (Aug 09 2025 at 20:25):
maybe you can
Aaron Liu (Aug 09 2025 at 20:25):
actually probably not
Aaron Liu (Aug 09 2025 at 20:25):
since what would the type be?
Aaron Liu (Aug 09 2025 at 20:26):
maybe just stick to the non-Qq stuff
Kenny Lau (Aug 12 2025 at 10:49):
import Mathlib
open Qq Lean
def unapp {u : Level} {G H : Q(Type u)} {mG : Q(Group $G)} {mH : Q(Group $H)}
(f : Q($G →* $H)) (y : Q($H)) : MetaM (Option Q($G)) := do
trace[debug] m!"{f}, {y}"
-- let ~q($f $x) := y | return .none
-- return .some y
have fx := y.getAppFnArgs
return (fx.snd)[5]?
axiom G : Type
axiom H : Type
@[instance] axiom gG : Group G
@[instance] axiom gH : Group H
axiom f : G →* H
axiom x : G
#eval unapp q(f) q(f x)
Kenny Lau (Aug 12 2025 at 10:49):
so I tried to use Qq to match f x where f is a group hom, and it doesn't work
Kenny Lau (Aug 12 2025 at 10:50):
the code above correctly extracts x from f x, without using Qq
Kenny Lau (Aug 12 2025 at 10:50):
the two commented lines are my Qq attempt, which has error (comment out the two lines below that)
Kenny Lau (Aug 12 2025 at 10:50):
how do I fix this?
Aaron Liu (Aug 12 2025 at 10:56):
Not sure if this is how you're supposed to do it
def unapp {u : Level} {G H : Q(Type u)} {mG : Q(Group $G)} {mH : Q(Group $H)}
(f : Q($G →* $H)) (y : Q($H)) : MetaM (Option Q($G)) := do
let ~q(DFunLike.coe «$f» $x) := y | return .none
return .some x
Kenny Lau (Aug 12 2025 at 10:57):
aha, we're back to the french quotes
Kenny Lau (Aug 12 2025 at 10:57):
aha, you don't even need coe
Kenny Lau (Aug 12 2025 at 10:57):
thanks!
Aaron Liu (Aug 12 2025 at 10:58):
The generated code just checks if it's reducibly defeq to f ?x where ?x is a fresh metavariable, without assigning any other metavariables
Last updated: Dec 20 2025 at 21:32 UTC