Zulip Chat Archive
Stream: Is there code for X?
Topic: Hybrid of `csimp` and `implemented_by`
cmlsharp (Dec 24 2025 at 22:58):
Is there an idiom for expressing "here's a proof that this efficient implementation is equivalent to this reference implementation, given this (necessarily unsafe) assumption that I promise is true"? The purpose is to "factor out" the unsafety into just a single assumption while still proving equivalence as much as is possible
I find myself writing this rather verbosely as
-- giving this a name because its common across several "unsafe" implementations
-- necessarily unprovable
def myAssumption : Prop := ...
unsafe def unsafeAssumeAssumption : myAssumption := lcProof
private def myFunEfficient (h : myAssumption) (input: a) := ...
private unsafe def myFunUnsafe (input: a) := myFunEfficient (unsafeAssumeAssumption)
-- public reference impl
@[implemented_by myFunUnsafe]
def myFun (input : a) := ...
-- keep myself honest
theorem myFunEfficient_eq_myFun (h : myAssumption) : myFun = myFunEfficient h := ...
I suppose I could cut down on the boilerplate with metaprogramming (something like @[csimp_unsafe_assume myAssumption] but I was wondering if there was an existing idiom for this.
Jakub Nowak (Dec 25 2025 at 00:28):
Is there even some example in core/batteries/mathlib where we have some kind of proof about the implementation referred to by implemented_by?
cmlsharp (Dec 25 2025 at 00:34):
I don’t know! I’m not sure how general this pattern is but I’ve been writing implementations of Array.scanl/scanr/scanlM/scanrM (planning on PRing batteries) and the correctness of the efficient versions solely rely on the (unprovable but true) assumption that arr.size < USize.size. It seems desirable to prove that the two versions are equivalent modulo this detail.
The fact that Array.foldl/foldr/et al don’t do this (and instead merely use implemented_by) led to a bug I reported recently.
Jakub Nowak (Dec 25 2025 at 00:36):
Now that we fixed these two bugs, surely, there are no more bugs. :grinning_face_with_smiling_eyes:
Jakub Nowak (Dec 25 2025 at 00:37):
You could make an unsafe csimp maybe? But I guess, the point is to have a proof not be unsafe, to ensure it's an honest proof.
Jakub Nowak (Dec 25 2025 at 00:40):
Though, you could always cheat with native_decide anyway, so maybe it's okay if you just read that there's only one use of unsafe lcProof? Something like this:
-- giving this a name because its common across several "unsafe" implementations
-- necessarily unprovable
def myAssumption : Prop := ...
unsafe def unsafeAssumeAssumption : myAssumption := lcProof
private def myFunEfficient (h : myAssumption) (input: a) := ...
private unsafe def myFunUnsafe (input: a) := myFunEfficient unsafeAssumeAssumption
-- public reference impl
def myFun (input : a) := ...
-- keep myself honest
theorem myFun_eq_myFunEfficient (h : myAssumption) : myFun = myFunEfficient h := ...
@[csimp]
unsafe def myFun_eq_myFunUnsafe : @myFun = @myFunUnsafe :=
myFun_eq_myFunEfficient unsafeAssumeAssumption
Jakub Nowak (Dec 25 2025 at 00:51):
My reasoning for using csimp over implemented_by is that it better communicates your intention that there is a proof (almost) that the implementation is safe.
Jakub Nowak (Dec 25 2025 at 00:56):
Oh, nvm, this doesn't work. "'unsafe' theorems are not allowed". But def works.
Jakub Nowak (Dec 25 2025 at 01:07):
Btw, where you able to make a proof? I've looked a bit at how hard arr.foldlM = arr.foldlMUnsafe would be though I gave up quickly.
cmlsharp (Dec 25 2025 at 01:12):
I was able to for an earlier version of scanl/scanr (haven’t tried the monadic versions). I’ve since changed the implementation a bit and the proof broke, but I don’t think that’s fundamental.
The main thing I’ve noticed is that USize is really annoying to work with in proofs (at least partially due to a lack of @[simp]/grind lemmas
cmlsharp (Dec 25 2025 at 01:48):
Jakub Nowak said:
Oh, nvm, this doesn't work. "'unsafe' theorems are not allowed".Butdefworks.
Oh interesting! I didnt realize csimp worked with defs but I guess that makes sense
cmlsharp (Dec 31 2025 at 07:29):
Jakub Nowak said:
Btw, where you able to make a proof? I've looked a bit at how hard
arr.foldlM = arr.foldlMUnsafewould be though I gave up quickly.
By the way, if interested I got the proof working for Array.scanlM (Array.scanrM is tougher because it diverges a bit more from the reference impl).
Last updated: Feb 28 2026 at 14:05 UTC