Zulip Chat Archive

Stream: new members

Topic: Easy program verification advice


V S (Sep 22 2025 at 20:45):

Hello! I hope this is the right channel for the question (a little bit intimidated to post in Program Verification since all the questions there seem very advanced and out of reach for me now).
So, I've been given a task by one of my professors - to verify an easy sorting algorithm on paper (which I have no problem with) and using a prover (which is where I need advice, since I've never done program verification and only now started slowly learning how to do it). I know the basic things in Lean and enjoy working with it and briefly remember Rocq. From what I've seen on Zulip, Lean is not as used in program verification since there are many Rocq extensions for that purpose and not all necessary packages are in Lean (is this is not true, please correct me, I'm extremely new to this), and I've also seen something that looks like a rocq verified file with the proof(?), although I couldn't really understand what was going on due to my lack of knowledge in the subject.
Now to my questions - 1) would you say that most (or at least easy) program verification is better done in Rocq and I should try and do the task only in rocq or is Lean also a very good option?
2) How hard would it be to take an algorithm (in whatever form is better suited for the verification), rewrite it in Lean and provide the proper verification?
3) Are there any helpful resources for this in Lean? I briefly remember seeing something like a manual here, but couldn't find it (or anything in google) now or in the other Lean manuals. Maybe a template or anything, really, since I have no clue on what to do or what examples to look for and recent enough post on Program verification mostly mention something scary-sounding and probably harder than what I'm going for

Anthony Wang (Sep 22 2025 at 21:39):

If you're verifying functional programs, Lean will probably work fine. But for imperative programs, you'll probably have a much easier time using a different proof assistant, such as Dafny which is specifically designed for that use case. It is also possible to verify imperative programs using Lean (see https://markushimmel.de/blog/my-first-verified-imperative-program/) but more complicated than doing it in Dafny.

(deleted) (Sep 23 2025 at 03:51):

Dafny is very bad

(deleted) (Sep 23 2025 at 03:52):

It relies on an SMT solver, and you need a lot of tinkering for assertions to go through

(deleted) (Sep 23 2025 at 03:52):

Lean is perfectly fine for program verification

(deleted) (Sep 23 2025 at 03:52):

Even for imperative programs

(deleted) (Sep 23 2025 at 03:54):

Just learn how to write programs in Lean. You'll do just fine.

(deleted) (Sep 23 2025 at 03:55):

If you get stuck, you can always post another question here. Good luck!

(deleted) (Sep 23 2025 at 03:57):

Yeah the link posted above is a very good starting point.

V S (Sep 23 2025 at 08:40):

Thank you both for the advice, will try and understand the blog post and write here if there are any more questions (or close once I get it completely :))

Anthony Wang (Sep 23 2025 at 16:07):

Huỳnh Trần Khanh said:

Dafny is very bad

I'm well aware of Dafny's flaws (I even discovered a major soundness bug in Dafny a few months ago that still hasn't been fixed yet), but it's still a very useful tool for program verification and much easier to learn and use than Lean.

Anthony Wang (Sep 23 2025 at 16:08):

Huỳnh Trần Khanh said:

It relies on an SMT solver, and you need a lot of tinkering for assertions to go through

I'd argue that Lean needs a lot of tinkering for tactic-based proofs to go through :upside_down:

Anthony Wang (Sep 23 2025 at 16:08):

But I guess this Dafny debate is getting a bit off-topic since this is the Lean Zulip after all.

Shreyas Srinivas (Sep 23 2025 at 22:27):

I can’t say much now, but in a few weeks or months there will be a few different ways to verify imperative programs in lean . That being said, the monadic verification framework might be worth a try right now

Shreyas Srinivas (Sep 23 2025 at 22:30):

I would argue that verifying a functional programming spec of this sorting procedure is bound to be valuable regardless of the developments on the imperative side

V S (Sep 24 2025 at 14:30):

So, yeah, I feel like I need to learn about the basics of monads to even write what program I want to verify :melting_face:
I've tried to formalize an easy bubble sort, but the function must return not Int or Bool, but List, and a quick search did nothing.
So I try to write

import Mathlib
def BubbleSort (l : List Real) : Id List Real := do

and get

Application type mismatch: In the application
  Id List
the argument
  List
has type
  Type ?u.6  Type ?u.6 : Type (?u.6 + 1)
but is expected to have type
  Type ?u.5 : Type (?u.5 + 1)

Can you please recommend something to learn the basics for this? I've checked some books about Lean and none had a proper theory about monads (although mentioned them)

Kenny Lau (Sep 24 2025 at 14:33):

@V S Id (List Real)

V S (Sep 24 2025 at 14:35):

And when I tried to copy the program from the link above

import Mathlib

def pairsSumToZero (l : List Int) : Id Bool := do

  let mut seen : HashSet Int := 

  for x in l do

    if -x  seen then

      return true

    seen := seen.insert x

  return false

I get

unknown identifier 'HashSet'

.

Kenny Lau (Sep 24 2025 at 14:35):

put open Std on a newline in Line 2

V S (Sep 24 2025 at 14:40):

@Kenny Lau Thank you so much!

V S (Sep 24 2025 at 15:02):

For my next attempt, I have

import Mathlib
open Std

def BubbleSort (l : List Real) : Id (List Real) := do
  let n := l.length()
  let a : Real := 0
  for i in List.range (n - 1) do
    for j in List.range (n - i - 1) do
      if l[j]! > l[j + 1]! then
        a := l[j]!
        l[j]! := l[j + 1]!
        l[j + 1]! := l[j]!
  return l

Which provides me with

type mismatch
  a = l[j]!
has type
  Prop : Type
but is expected to have type
  Id PUnit.{1} : Type

Which seems weird. Also, when trying to understand how to access the i-th element, I've seen a topic about proving that it's valid, but was also unable to understand how to prove that it's valid exactly.
I hope I'm not bombarding the topic with stupid questions :melting_face:

Kenny Lau (Sep 24 2025 at 15:06):

@V S your code does not result in your posted error

Kenny Lau (Sep 24 2025 at 15:07):

also you don't net () for length, this isn't java, everything is a function here

V S (Sep 24 2025 at 15:14):

Kenny Lau said:

V S your code does not result in your posted error

Unfortunately, I can't use the live.lean-lang.org playground and lean.math.hhu.de returns an abysmal error for import Mathlib, so I'm using the version on my pc which does give this error. I have the last lean Vscode extension, do I need to do the usual lake ... stuff to try and get rid of it?

Kenny Lau (Sep 24 2025 at 15:15):

V S said:

I can't use the live.lean-lang.org playground

why?

V S (Sep 24 2025 at 15:21):

Kenny Lau said:

V S said:

I can't use the live.lean-lang.org playground

why?

It won't load on any of my devices (with or without proxy) so I've kinda given up on it and usually have no trouble with using either lean.math.hhu.de or a local version on pc, this is the first time something like this happened

Julia Scheaffer (Sep 24 2025 at 15:22):

Is lean on your PC out of date?

V S (Sep 24 2025 at 15:25):

I think the .de Duper mode works? and it gives me the unsupported pattern in syntax match l[j]! error after I changed let mut a : Real := 0, is it the same as what the .org playground says? (I also tried to use let mut r := l and do everything for r, but it didn't help)

Julia Scheaffer (Sep 24 2025 at 15:26):

It is, yes

V S (Sep 24 2025 at 15:26):

Julia Scheaffer said:

It is, yes

Ok, thank you! Will use it then until the Lean on my pc updates

Kenny Lau (Sep 24 2025 at 15:28):

@V S the function to change one element of a list is List.set

V S (Sep 24 2025 at 15:36):

Kenny Lau said:

V S the function to change one element of a list is List.set

This gives

type mismatch
  l.set j l[j + 1]!
has type
  List  : Type
but is expected to have type
  Id PUnit.{1} : Type

. Is it a valid error or do I have to find an alternative to Duper too?

MWE (the latest version so that the code is available):

import Mathlib
open Std

def BubbleSort (l : List Real) : Id (List Real) := do
  let mut r := l
  let n := l.length()
  let mut a : Real := 0
  for i in List.range (n - 1) do
    for j in List.range (n - i - 1) do
      if r[j]! > r[j + 1]! then
        a := r[j]!
        r := r.set j r[j+1]!
        r := r.set (j + 1) a
  return r

Kenny Lau (Sep 24 2025 at 15:36):

can you post full code?

Kenny Lau (Sep 24 2025 at 15:36):

List.set returns a list, not unit

Kenny Lau (Sep 24 2025 at 15:36):

(return unit is the equivalent of java's "void method")

Julia Scheaffer (Sep 24 2025 at 15:37):

You would need to use l := l.set ... right?

V S (Sep 24 2025 at 15:37):

IT WORKED!!!

V S (Sep 24 2025 at 15:38):

Thank you everyone so much! Off I go to work with Hoare triples

Anthony Wang (Sep 24 2025 at 15:39):

To swap two elements, you can also use l := l.swap j (j + 1) instead of manually swapping with a temporary variable.

V S (Sep 24 2025 at 15:42):

Anthony Wang said:

To swap two elements, you can also use l := l.swap j (j + 1) instead of manually swapping with a temporary variable.

It gives

invalid field 'swap', the environment does not contain 'List.swap'
  r
has type
  List 

and I can't find it in Mathlib

Anthony Wang (Sep 24 2025 at 15:43):

Oh my bad, swap only works on arrays, not lists.

Robin Arnez (Sep 24 2025 at 15:43):

You're probably better off using Arrays still because on List indexing is O(n)

V S (Sep 24 2025 at 15:49):

Robin Arnez said:

You're probably better off using Arrays still because on List indexing is O(n)

Rewriting with arrays now! By the way, how do you properly prove that the indices are correct to use? r[j] ⟨by decide⟩ or something like that does not work

Aaron Liu (Sep 24 2025 at 15:58):

V S said:

Robin Arnez said:

You're probably better off using Arrays still because on List indexing is O(n)

Rewriting with arrays now! By the way, how do you properly prove that the indices are correct to use? r[j] ⟨by decide⟩ or something like that does not work

You need to pull the proofs into the loop with for hi : i in ...

Aaron Liu (Sep 24 2025 at 16:00):

You also need to encode the length of the arrays into their type with docs#Vector or similar so they can't change length on you

Aaron Liu (Sep 24 2025 at 16:00):

Then you can probably use r[i]'(by grind)

Julia Scheaffer (Sep 24 2025 at 16:00):

I am learning so much in this thread :smile:

V S (Sep 24 2025 at 16:06):

Aaron Liu said:

You also need to encode the length of the arrays into their type with docs#Vector or similar so they can't change length on you

I unfortunately don't really get how to encode the length :( The last MWE is:

import Mathlib
open Std Real List

def BubbleSort (l : Array Real) : Id (Array Real) := do
  let mut a : Real := 0
  let mut r := l
  let n := l.size
  for hi: i in List.range (n - 1) do
    for hj: j in List.range (n - i - 1) do
      if r[j]'(by grind) > r[j + 1]'(by grind) then
        r := r.swap j (j+1)
  return r

and it returns errors even with hi

V S (Sep 24 2025 at 16:09):

There is Array.toVector, but I don't really get how it makes the length constant

Anthony Wang (Sep 24 2025 at 16:09):

You need to use Vector Real n instead of Array Real.

Anthony Wang (Sep 24 2025 at 16:10):

(deleted)

Julia Scheaffer (Sep 24 2025 at 16:13):

Here's a version I got working based on @V S 's

import Mathlib

def BubbleSort {α} [LT α] [DecidableLT α] (a: Array α) : Array α := Id.run do
  let n := a.size
  let mut a : Vector α n := a.toVector
  for hi : i in List.range (n - 1) do
    for hj : j in List.range (n - i - 1) do
      if a[j]'(by grind) > a[j + 1]'(by grind) then
        a := a.swap j (j+1) (by grind) (by grind)
  return a.toArray

#eval BubbleSort #[3, 8, 5]

Julia Scheaffer (Sep 24 2025 at 16:14):

lots of grinding, but that can be optimized

Julia Scheaffer (Sep 24 2025 at 16:16):

Now just to try and prove that it produces sorted arrays :sweat_smile:

V S (Sep 24 2025 at 16:17):

Julia Scheaffer said:

Here's a version I got working based on V S 's

import Mathlib

def BubbleSort {α} [LT α] [DecidableLT α] (a: Array α) : Array α := Id.run do
  let n := a.size
  let mut a : Vector α n := a.toVector
  for hi : i in List.range (n - 1) do
    for hj : j in List.range (n - i - 1) do
      if a[j]'(by grind) > a[j + 1]'(by grind) then
        a := a.swap j (j+1) (by grind) (by grind)
  return a.toArray

#eval BubbleSort #[3, 8, 5]

That's great! Unfortunately, Duper does not like this grinding, but I trust that it's correct (guess I will try and hope that it works at least somewhere on my devices :smiling_face_with_tear: )

Julia Scheaffer (Sep 24 2025 at 16:18):

I am trying to make a version that doesn't use grind now

Julia Scheaffer (Sep 24 2025 at 16:19):

Got it working without grinding

import Mathlib

def BubbleSort {α} [LT α] [DecidableLT α] (a: Array α) : Array α := Id.run do
  let n := a.size
  let mut a : Vector α n := a.toVector
  for hi : i in List.range (n - 1) do
    for hj : j in List.range (n - i - 1) do
      have := List.mem_range.mp hi
      have := List.mem_range.mp hj
      if a[j] > a[j + 1] then
        a := a.swap j (j+1)
  return a.toArray

#eval BubbleSort #[3, 8, 5]

Julia Scheaffer (Sep 24 2025 at 16:20):

Just needed to enter some facts about i and j into the context

Julia Scheaffer (Sep 24 2025 at 16:21):

Does that version run on your machine @V S ?

V S (Sep 24 2025 at 16:22):

Julia Scheaffer said:

Does that version run on your machine V S ?

It absolutely does!

Julia Scheaffer (Sep 24 2025 at 17:05):

I was looking at the docs on List.Vector and many of the definitions seems to have links to Vector instead of List.Vector (example: List.Vector.«term_::ᵥ_» ) is this a bug in the docs?

Kenny Lau (Sep 24 2025 at 17:12):

yes

Julia Scheaffer (Sep 24 2025 at 17:19):

It it a known bug or is there somewhere i should report it?

Kenny Lau (Sep 24 2025 at 17:20):

you could probably discuss this in #general

Julia Scheaffer (Sep 24 2025 at 17:20):

Should I just move my message to a new thread in #general then?

Julia Scheaffer (Sep 24 2025 at 17:20):

Sorry, i am sort of new to Zulip

Kenny Lau (Sep 24 2025 at 17:23):

Just copy the message manually.

Ruben Van de Velde (Sep 24 2025 at 17:39):

You can create a PR directly

V S (Oct 01 2025 at 13:06):

Ok, I am back with questions. I started to follow the link again. My latest attempt (formulation of the theorem) is:

import Mathlib

def BubbleSort {α} [LT α] [DecidableLT α] (a: Array α) : Array α := Id.run do
  let n := a.size
  let mut a : Vector α n := a.toVector
  for hi : i in List.range (n - 1) do
    for hj : j in List.range (n - i - 1) do
      have := List.mem_range.mp hi
      have := List.mem_range.mp hj
      if a[j] > a[j + 1] then
        a := a.swap j (j+1)
  return a.toArray

theorem BubbleSort_corr {α} [LT α] [DecidableLT α] (a: Array α) : a.size >= 0 BubbleSort a  i j : , i <= a.size - 1  j <= i -> a[j] <= a[i]⦄ := by
  mvcgen [BubbleSort]

since the ⦃Precondition⦄ is that the size of the array is >= 0 and the ⦃Postcondition⦄ has 2 conditions:

  1. The result is a permutation of the original array which I have no idea how to formalize since we have the .toVector (edit: I missed that we have the .toArray at the end so something like 'for each element of a, there is an unique element of the result such that they are equivalent')
  2. The result is ordered which I wrote as above

I get unexpected token '>='; expected ':' (and if I copy ⦃⌜True⌝⦄ like from the link, it also gives an error of a missing token, which is confusing). So I am once again asking for help with the first of conditions (it seems obvious but I feel like it would be cheating just to omit it) and with the syntax error

Kenny Lau (Oct 01 2025 at 13:20):

@V S if you look at the link in your link that says "Click here to open the online Lean playground pre-filled with the proof", you will see open Std Do on Line 4, which you also need to do to enable that notation.

Anthony Wang (Oct 01 2025 at 13:24):

This fixes some of your problems (although Lean is still complaining about the array indices):

import Std.Tactic.Do
import Mathlib

open Std.Do

def BubbleSort' {α} [LT α] [DecidableLT α] (a: Array α) : Id (Array α) := do
  let n := a.size
  let mut a : Vector α n := a.toVector
  for hi : i in List.range (n - 1) do
    for hj : j in List.range (n - i - 1) do
      have := List.mem_range.mp hi
      have := List.mem_range.mp hj
      if a[j] > a[j + 1] then
        a := a.swap j (j+1)
  return a.toArray

theorem BubbleSort_corr {α} [LT α] [DecidableLT α] (a: Array α) : True BubbleSort' a  r => ⌜∀ i j : , i < r.size  j <= i -> ¬(r[j] > r[i]) := by
  mvcgen

However I suspect even if you prove the array indices are in bounds, you'll run into the problem #new members > mvcgen doesn't produce any invariant goals.

V S (Oct 01 2025 at 13:26):

Kenny Lau said:

V S if you look at the link in your link that says "Click here to open the online Lean playground pre-filled with the proof", you will see open Std Do on Line 4, which you also need to do to enable that notation.

Thank you! I completely missed the 'link in the link'

V S (Oct 01 2025 at 13:28):

Anthony Wang said:

This fixes some of your problems (although Lean is still complaining about the array indices):

import Std.Tactic.Do
import Mathlib

open Std.Do

def BubbleSort' {α} [LT α] [DecidableLT α] (a: Array α) : Id (Array α) := do
  let n := a.size
  let mut a : Vector α n := a.toVector
  for hi : i in List.range (n - 1) do
    for hj : j in List.range (n - i - 1) do
      have := List.mem_range.mp hi
      have := List.mem_range.mp hj
      if a[j] > a[j + 1] then
        a := a.swap j (j+1)
  return a.toArray

theorem BubbleSort_corr {α} [LT α] [DecidableLT α] (a: Array α) : True BubbleSort' a  r => ⌜∀ i j : , i < r.size  j <= i -> ¬(r[j] > r[i]) := by
  mvcgen

However I suspect even if you prove the array indices are in bounds, you'll run into the problem #new members > mvcgen doesn't produce any invariant goals.

That's a little bit disheartening, I'll follow your discussion and hope that we will get our answers or hints soon...

Anthony Wang (Oct 01 2025 at 13:32):

My recommendation would be to use Dafny instead of Lean since in your original post since you mentioned you didn't need to specifically use Lean for your task. It seems like Lean's mvcgen still has some limitations and definitely isn't as user-friendy as Dafny.

V S (Oct 01 2025 at 13:39):

Anthony Wang said:

My recommendation would be to use Dafny instead of Lean since in your original post since you mentioned you didn't need to specifically use Lean for your task. It seems like Lean's mvcgen still has some limitations and definitely isn't as user-friendy as Dafny.

I'll definitely check it out! Do you know whether it's explicitly stated or discussed on what level Dafny is 'trustable', similar to https://ammkrn.github.io/type_checking_in_lean4/trust/trust.html?

Ruben Van de Velde (Oct 01 2025 at 14:44):

That seems like an odd comment to make when people ask for help on the lean zulip, but okay

Anthony Wang (Oct 01 2025 at 15:19):

V S said:

I'll definitely check it out! Do you know whether it's explicitly stated or discussed on what level Dafny is 'trustable', similar to https://ammkrn.github.io/type_checking_in_lean4/trust/trust.html?

You should probably ask this on the Dafny Zulip to get a better answer, but the short answer is that Dafny is not very trustworthy since it doesn't satisfy the de Bruijn criterion and Dafny has had many soundness bugs which often take a while to get fixed.


Last updated: Dec 20 2025 at 21:32 UTC