How to write correct code by construction using the Coq Proof Assistant
betterprogramming.pub
betterprogramming.pub
Coq and Isabelle also see some use, but they are more costly to use in my experience.
[0] https://www.youtube.com/playlist?list=PLre5AT9JnKShFK9l9HYzk...
My goal with this tutorial was to introduce the core aspects of the language (dependent types, tactics, etc.) from first principles. If you're fascinated by proof assistants like Coq or Lean and want to understand how to use them, this tutorial is written for you.
Any feedback is appreciated!
Your tutorial is very interesting, but in comparison to the Software Foundations series you have relatively few exercises and they're all at the end of the chapter. I found it helpful to do exercises as I went along, in many cases testing or exercising my knowledge of a new concept or Coq feature right away. So I would suggest writing more exercises and sprinkling them closer to where some of the material is introduced.
Also, some of your exercises in some chapters have a pretty steep ramp-up in difficulty, so again it might be nice to have exercises that gradually build in difficulty.
These are probably important considerations when readers are going to be working through this material entirely on their own and it might be their first exposure to Coq. (I think your tutorial says it was previously presented in-person, which has got to be a lot easier, since people can ask questions!)
Thanks for writing and sharing this material!
One thing that the post does not mention is that things get nasty real quick if you use actual machine integers (or number generally) instead of ideal ones in Coq- though that can also be due to my own ignorance as an amateur.
http://michaeldnahas.com/doc/nahas_tutorial.html
It has been mentioned on HN before (multiple times).
Theorem NestedTreeContains1:
forall (t: tree),
t = Node example 2 (Node Nil 3 Nil)
->
contains 1 t = true.
Proof.nail.
wreck t.
- wat.
- wreck H into Ht1, Hv and Ht2.
sub Hv.
evaluate.
sub Ht1.
just ExampleTreeContains1.
Qed.Theorem, forall, qed look fairly likely. Are there built-in operations in coq called 'wat' and 'wreck'?
nail ~= intros
wat ~= discriminate
etc.It boggles the mind why the author would make up fanciful new names for the fundamental tactics in this "how to" article.
If you are giving a talk on a theorem proving programing language you're gonna want to make it fun
One thing I think is important about Coq is that it really is meant to be interactive. Even after a fair amount of usage I still find it somewhat tricky to just read a proof, especially a nontrivial one. A good IDE will show you a panel that includes all your hypothesis and requirements so far, updating as you run each line
But the most important thing to understand about Coq is that it's meant to be read in an IDE. The IDE will show you, at each step, what has been proved and what remains to be proved. The "tactics" you see above basically mean, "At this point in the proof, we can make progress by substituting Hv. And in context, this will make sense.
But without being able to see the proof state at each step, Coq can be incredibly cryptic.
In fact it often feels that way. You're either asked to extend or improve existing constructions that feel like sticks and stones bound together with rubber bands. Or one feels excited for being allowed to make up such a construction as a demo, not knowing it will be used as a basis for a production-quality construction.
I'm not sure if mathematical proof is the way to go. There's just too much code to cover for this approach to be realistic.
Most problems are born on the drawing table. The designs seem right on paper and only in practice, during or after building, they become visable.
It may be unrealistic, but people are doing it anyway. It may be the case that there are just a few Leslie Lamports capable of doing this stuff, and it's unrealistic to expect the rest of us to.
But even if that's the case, I still want them to publish their code as libraries, so I can reach for tools that will work - even if I couldn't have proved it myself.
And if this whole mathematical/proofy way of doing things is a dead end (which I strongly disagree with - we're just at the beginning!) then it's no big deal, because we are already really good at turning JSON into stack traces.
1. The problem expressed here has to do with a particular, well understood problem. It is not subject to the whims or constraint needs of the "customer". Proof construction is great when the problem is properly understood, I don't know how well it handles constantly changing requirements. (This encompasses business and interaction constraints too - perhaps you can construct a provable correct UI, but will it be engaging or usable?)
2. Test driven development can guide high quality code construction, arguably at lower construction cost. That is the reason I'd argue this has not taken off in comparison to traditional "test" based design.
You can argue it produces lower quality code, and you'd probably be right. But in comparison to construction of complex, rich design, I suspect that it produces faster results at lower cost. The provable solution will win out...eventually. Depends on whether it can make significant marker penetration once it is complete.
Nevertheless, yes of course we should be building foundations witb concrete not plywood and Styrofoam, so this is a good development. I do think a proper engineering approach with test based verification can build a "somewhat concrete" foundation, at least.
Personally, this article didn't strike me as being very easy to follow, or as being the most compelling demonstration of the value of formal methods. At the risk of going off-topic: if you're not seeing the point, I recommend this example using SPARK, a non-functional language. [1]
[0] https://betterprogramming.pub/a-taste-of-coq-and-correct-cod...
[1] https://docs.adacore.com/spark2014-docs/html/ug/en/tutorial....
In the section “Correct BST by Construction”, they explain the technique of requiring every constructor of a BST to supply a proof that it maintains the properties of a BST. Any modification to a BST constructs a new BST (since Coq is pure), so any operations on a BST must also supply a proof of their correctness. That is correct by construction.
Currently, a lot of instances of the first situation get the treatment befitting the second. This is because it's still too hard to use these tools at scale. However, the trend is certainly towards improving tooling and the cost of doing this will go down a lot.
Maybe in a hundred years programming will be replaced by either "formally specify what you want", if you know what you want, (AI fills in implementation of how it does what you want, as well as a proof that it does) or, if you only very roughly know what you want, explain it to the chatbot and answer it's questions.
Even today, in limited well-defined problem spaces, there are sometimes good algorithms for this sort of thing. See Dana Angluin's algorithm for learning the DFA that recognizes a regular language given through an interactive process of having the user provide examples/counterexamples, which serve to provide an increasingly constrained spec.
Great.
Does the formal spec represent a complete and accurate description of what the code is supposed to do?
shrugs
Even if you can understand what the proof proves (not a given) do you personally understand the actual requirements for the software perfectly?
If you employed this approach, I would want to pair it at a minimum with a test suite.
Even then, how do you know that your system (covered by hill climbing suites of tests that pass), has correct behavior for all things you didn't specify?
You don't. Better to build from the ground up formally than fight with "AI" that probably got it worse than wrong first try.
I hope your pessimism is founded though, I would really like to see programming by human advance rather than the whole thing be taken over by AI. Hybrid approaches are also possible (AI in the loop but not in the core position).