Zulip Chat Archive
Stream: new members
Topic: decreasing_by in monadic context?
Marcus Völker (Jan 03 2026 at 13:02):
Hello world, new Lean user here.
I'm trying to prove that a function I wrote is well-founded, but I can't get decreasing_by to give me all the context necessary to prove this.
The function itself looks like this:
def manyFunc {τ α} (p : StrongParser τ α) (t : List τ) : DCont String (List α × List τ) :=
let val := p.val
let H := p.property
do
let (a0,r) <- pFunc val t
(λ(a,r') => (a0 :: a, r')) <$> manyFunc p r
termination_by t.length
decreasing_by
sorry
In the goal state, I would have to prove (correctly) that r.length < t.length, but r essentially appears completely unconstrained.
MWE:
inductive DCont (ε α) where
| Intro (run : (ρ : Type) → (α → ρ) → (ε → ρ) → ρ)
def run {ε α} (c : DCont ε α) := match c with
| DCont.Intro r => r
instance {ε : Type u} : Functor (DCont ε) where
map f c := DCont.Intro (λ ρ => λ bf => λ ef => run c ρ (bf ∘ f) ef)
instance {ε : Type u} : Applicative (DCont ε) where
pure a := DCont.Intro (λ _ => λ af => λ _ => af a)
seq cf ca := DCont.Intro (λ ρ => λ bf => λ ef => run cf ρ (λ f => run (ca ()) ρ (bf ∘ f) ef) ef)
instance {ε : Type u} : Monad (DCont ε) where
bind x f := DCont.Intro (λρ => λbf => λef => run x ρ (λc => run (f c) ρ bf ef) ef)
def pFunc_is_dec {τ α} (pFunc : List τ → DCont String (α × List τ)) := ∀t, run (pFunc t) Bool (λ(_,r) => r.length <= t.length) (λ_ => true)
def pFunc_is_sdec {τ α} (pFunc : List τ → DCont String (α × List τ)) := ∀t, run (pFunc t) Bool (λ(_,r) => r.length < t.length) (λ_ => true)
inductive Parser (τ α) where
| Intro (pFunc : List τ → DCont String (α × List τ)) (Hdec : pFunc_is_dec pFunc)
def pFunc {τ α} (p : Parser τ α) := match p with
| Parser.Intro f _ => f
def StrongParser (τ α) := { p : Parser τ α // pFunc_is_sdec (pFunc p) }
def manyFunc {τ α} (p : StrongParser τ α) (t : List τ) : DCont String (List α × List τ) :=
let val := p.val
let H := p.property
do
let (a0,r) <- pFunc val t
(λ(a,r') => (a0 :: a, r')) <$> manyFunc p r
termination_by t.length
decreasing_by
sorry
I presume that the inclusion of the monad and the indirection of the continuation doesn't help here, but is there a way to get lean to give me more about r for this proof?
Eric Wieser (Jan 03 2026 at 13:14):
It doesn't answer your question, but
inductive DCont (ε α) where
| Intro (run : (ρ : Type) → (α → ρ) → (ε → ρ) → ρ)
def run {ε α} (c : DCont ε α) := match c with
| DCont.Intro r => r
can be more concisely written as
structure DCont (ε α) where
intro ::
run : (ρ : Type) → (α → ρ) → (ε → ρ) → ρ
Eric Wieser (Jan 03 2026 at 13:14):
(and similarly for Parser)
Eric Wieser (Jan 03 2026 at 13:18):
My suspicion is that you can't prove termination (because it doesn't terminate), since pFunc_is_dec doesn't protect against the case where pFunc inspects its continuation, and only produces a smaller list when (λ(_,r) => r.length <= t.length) is passed in, otherwise producing a larger one
Eric Wieser (Jan 03 2026 at 13:21):
I would speculate that
def pFunc_is_dec {τ α} (pFunc : List τ → DCont String (α × List τ)) :=
∀ t, SatisfiesM (·.2.length ≤ t.length) (pFunc t)
is the right condition
Mirek Olšák (Jan 03 2026 at 14:27):
I think you want to build an alternative function to pFunc which operates on a StrongParser, and returns also a proof of the result being smaller. So it would look something like
def spFunc {τ α} (p : StrongParser τ α) (l : List τ) : DCont String {x : (α × List τ) // x.2.length < l.length} := do
let ⟨a0,r⟩ ← pFunc p.val l
return ⟨⟨a0,r⟩, sorry⟩
def manyFunc {τ α} (p : StrongParser τ α) (t : List τ) : DCont String (List α × List τ) :=
do
let ⟨⟨a0,r⟩,h⟩ <- spFunc p t
(λ(a,r') => (a0 :: a, r')) <$> manyFunc p r
termination_by t.length
decreasing_by exact h
Only that if you do it like this, spFunc indeed doesn't have enough context to prove the sorry. I would try to avoid the monad syntax, and figure out on a term-level if all the necessary information can be passed (and if it is even true).
Mirek Olšák (Jan 03 2026 at 14:29):
And just to be sure, you know that if the only purpose for the code is running (and not proving), you could use partial def and skip proving termination, right?
Marcus Völker (Jan 03 2026 at 14:34):
@Eric Wieser Thank you for the suggestions, I will rethink what parser termination actually means.
@Mirek Olšák That might be a good idea! Yes, I know I don't need to prove termination, but this is as much for me learning Lean as it is for producing a viable parser infrastructure, so I actually want to prove termination if I can (plus, I like the added guarantees that I get about the code if I manage the proof)
Last updated: Feb 28 2026 at 14:05 UTC