Literate programming with Alectryon (Coq input)
Alectryon supports literate programs and documents (combinations of code and prose) written in Coq and reStructuredText. Here is an example, written in Coq. It can be converted to reST, HTML, or LaTeX using the following commands:
alectryon literate_coq.v
# Coq+reST → HTML; produces ‘literate_coq.html’
alectryon --html-dialect=html5 literate_coq.v \
-o literate_coq.5.html
# Coq+reST → HTML5; produces ‘literate_coq.5.html’
$ DOCUTILSCONFIG=literate.docutils.conf alectryon \
literate_coq.v --backend latex
# Coq+reST → LaTeX; produces ‘literate_coq.tex’
$ DOCUTILSCONFIG=literate.docutils.conf alectryon \
literate_coq.v --backend latex --latex-dialect lualatex \
-o literate_coq.lua.tex
# Coq+reST → LuaLaTeX; produces ‘literate_coq.lua.tex’
alectryon literate_coq.v --backend rst
# Coq+reST → reST; produces ‘literate_coq.v.rst’
$ cd ..; python -m alectryon.literate --from coq --to rst \
recipes/literate_coq.v > recipes/literate_coq.min.rst
# Minimal Coq → reST; produces ‘literate_coq.min.rst’
$ cd ..; python -m alectryon.literate --from coq --to rst - \
< recipes/literate_coq.v > recipes/literate_coq.min.stdin.rst
# Minimal Coq → reST; produces ‘literate_coq.min.stdin.rst’
Here's an inductive specification of evenness:
Inductive Even : nat -> Prop :=
| EvenO : Even O
| EvenS : forall n, Even n -> Even (S (S n)).… and a corresponding decision procedure:
Fixpoint even (n: nat): bool := match n with | 0 => true | 1 => false | S (S n) => even n end. (* Ensure that we never unfold [even (S n)] *) Arguments even : simpl nomatch.
Can we prove it correct?
forall n : nat, even n = true <-> Even nforall n : nat, even n = true <-> Even neven 0 = true <-> Even 0n: nat
IHn: even n = true <-> Even neven (S n) = true <-> Even (S n)true = true <-> Even 0n: nat
IHn: even n = true <-> Even neven (S n) = true <-> Even (S n)true = true <-> Even 0all: constructor.true = true -> Even 0Even 0 -> true = truen: nat
IHn: even n = true <-> Even neven (S n) = true <-> Even (S n)
The induction hypothesis doesn't apply — maybe we need to destruct n?
IHn: even 0 = true <-> Even 0even 1 = true <-> Even 1n: nat
IHn: even (S n) = true <-> Even (S n)even (S (S n)) = true <-> Even (S (S n))split; inversion 1.IHn: even 0 = true <-> Even 0even 1 = true <-> Even 1n: nat
IHn: even (S n) = true <-> Even (S n)even (S (S n)) = true <-> Even (S (S n))
Stuck again!
Abort.Strengthening the spec
The usual approach is to strengthen the spec to work around the weakness of the inductive principle.
forall n : nat, (even n = true <-> Even n) /\ (even (S n) = true <-> Even (S n))forall n : nat, (even n = true <-> Even n) /\ (even (S n) = true <-> Even (S n))(true = true <-> Even 0) /\ (false = true <-> Even 1)n: nat
IHn: (even n = true <-> Even n) /\ (even (S n) = true <-> Even (S n))(even (S n) = true <-> Even (S n)) /\ (even n = true <-> Even (S (S n)))(true = true <-> Even 0) /\ (false = true <-> Even 1)true = true -> Even 0false = true -> Even 1Even 1 -> false = trueall: inversion 1.false = true -> Even 1Even 1 -> false = truen: nat
IHn: (even n = true <-> Even n) /\ (even (S n) = true <-> Even (S n))(even (S n) = true <-> Even (S n)) /\ (even n = true <-> Even (S (S n)))n: nat
Hne: even n = true -> Even n
HnE: Even n -> even n = true
HSne: even (S n) = true -> Even (S n)
HSnE: Even (S n) -> even (S n) = true(even (S n) = true <-> Even (S n)) /\ (even n = true <-> Even (S (S n)))n: nat
Hne: even n = true -> Even n
HnE: Even n -> even n = true
HSne: even (S n) = true -> Even (S n)
HSnE: Even (S n) -> even (S n) = trueeven (S n) = true -> Even (S n)n: nat
Hne: even n = true -> Even n
HnE: Even n -> even n = true
HSne: even (S n) = true -> Even (S n)
HSnE: Even (S n) -> even (S n) = trueEven (S n) -> even (S n) = truen: nat
Hne: even n = true -> Even n
HnE: Even n -> even n = true
HSne: even (S n) = true -> Even (S n)
HSnE: Even (S n) -> even (S n) = trueeven n = true -> Even (S (S n))n: nat
Hne: even n = true -> Even n
HnE: Even n -> even n = true
HSne: even (S n) = true -> Even (S n)
HSnE: Even (S n) -> even (S n) = trueEven (S (S n)) -> even n = trueinversion 1; eauto. Qed.n: nat
Hne: even n = true -> Even n
HnE: Even n -> even n = true
HSne: even (S n) = true -> Even (S n)
HSnE: Even (S n) -> even (S n) = trueEven (S (S n)) -> even n = true
Writing a fixpoint
But writing a fixpoint (either with the Fixpoint command or with the fix tactic) is much nicer:
even_Even_fp: forall n : nat, even n = true <-> Even n
n: nateven n = true <-> Even neven_Even_fp: forall n : nat, even n = true <-> Even n
n: nateven n = true <-> Even neven_Even_fp: forall n : nat, even n = true <-> Even ntrue = true <-> Even 0even_Even_fp: forall n : nat, even n = true <-> Even nfalse = true <-> Even 1even_Even_fp: forall n : nat, even n = true <-> Even n
n: nateven n = true <-> Even (S (S n))repeat constructor.even_Even_fp: forall n : nat, even n = true <-> Even ntrue = true <-> Even 0split; inversion 1.even_Even_fp: forall n : nat, even n = true <-> Even nfalse = true <-> Even 1even_Even_fp: forall n : nat, even n = true <-> Even n
n: nateven n = true <-> Even (S (S n))even_Even_fp: forall n : nat, even n = true <-> Even n
n: nateven n = true -> Even (S (S n))even_Even_fp: forall n : nat, even n = true <-> Even n
n: natEven (S (S n)) -> even n = trueconstructor; apply even_Even_fp; assumption.even_Even_fp: forall n : nat, even n = true <-> Even n
n: nateven n = true -> Even (S (S n))inversion 1; apply even_Even_fp; assumption. Qed.even_Even_fp: forall n : nat, even n = true <-> Even n
n: natEven (S (S n)) -> even n = true
Alectryon defines a :coqid: role to link to definitions in Coqdoc-generated documentation; for example:
Note that the standard library already contains a boolean predicate for
even(called Coq.Init.Nat.even, or even for short), as well as an inductive one (called Nat.Even in module Coq.Arith.PeanoNat).
If you have your own documentation, you can use a custom role to point to your own URL:
With this definition, for example, you can write Nat.Even instead of Nat.Even.