To compile:
$ alectryon references.rst # ReST → HTML; produces ‘references.html’ $ DOCUTILSCONFIG=references.docutils.conf alectryon \ references.rst -o references.xe.tex --latex-dialect xelatex # ReST → HTML; produces ‘references.xe.tex’
Alectryon supports references to individual sentences and hypotheses within a code fragment. The easiest way to reference a sentence is to use :mref:`search-term`. Alectryon will search for that text and automatically add a label to the first matching sentence of the proof. For example:
plus_comm: forall n m : nat, n + m = m + n
n, m: natn + m = m + nplus_comm: forall n m : nat, n + m = m + n
n, m: natn + m = m + nplus_comm: forall n m : nat, n + m = m + n9
n, m: nat3
Heq: n = 04
20 + m = m + 05plus_comm: forall n m : nat, n + m = m + n
n, m, n0: nat
Heq: n = S n07S n0 + m = m + S n08rewrite <- plus_n_O; reflexivity.plus_comm: forall n m : nat, n + m = m + n
n, m: nat
Heq: n = 00 + m = m + 0The
Fixpoint
command (1) indicates that we are beginning an inductive proof.
Optionally, the label can be picked manually, using :mref:`label <target>`:
The proof starts with a case analysis, indicated by “◉”.
Instead of whole sentences, is possible to refer to individual goals and hypotheses:
In the first case (2), we see the variablen
in the context (3), and we see that it is0
(4); notice how the conclusion of the first goal 5 does not mentionn
(it says0
instead). In the second case 6, we see thatn
equalsS n0
(7) and the conclusion (8) mentionsS n0
instead of0
.
Note how the reference to a hypothesis of the second goal caused the whole hypothesis block to be unfolded (by default, hypotheses of secondary goals are folded).
As expected, referring multiple times to the same object creates a single marker, even using different references:
To allow forward- and back-references, counters are not reset from one block to the next:
plus_comm: forall n m : nat, n + m = m + n
n, m, n0: nat
Heq: n = S n0S n0 + m = m + S n0plus_comm: forall n m : nat, n + m = m + n
n, m, n0: nat
Heq: n = S n0S (n0 + m) = m + S n0plus_comm: forall n m : nat, n + m = m + n
n, m, n0: nat
Heq: n = S n0S (m + n0) = m + S n0reflexivity. Qed.plus_comm: forall n m : nat, n + m = m + n
n, m, n0: nat
Heq: n = S n0m + S n0 = m + S n0
Custom counter styles can be defined like using the .. role:: directive and the :counter-style: option:
Here is how it looks:
The following commands print information about an identifier α, print its definition β, and compute the type of a term γ or its reduction δ.
The second batch of commands perform reduction with a custom strategy: い ろ は に ほ へ.
Each inline reference is a link to the corresponding code fragment.
Objects located using the marker-placement mini-language can be tagged with arbitrary properties by appending a [key]=val annotation to the placement expression. For example:
These properties can then be used within custom transforms. Out of the box, Alectryon only recognizes the [lang] annotation; if it is found, the corresponding code is highlighted using the Pygments lexer for that language.
Extraction Language Haskell.
Instead of inserting a link to the relevant goal fragment, you can use the :mquote: role to insert a copy of a goal fragment inline. This only works for an input sentence, the conclusion or name of a goal, and the type, body, or name of a hypothesis:
The proof above had two cases: Heq: n = 0 (4) and Heq: n = S n0 (7). The second goal below is named gg. The last case of the proof below has two induction hypotheses: List.In a l -> List.In a l' and List.In a l' -> List.In a l''. The two permutation hypotheses are H and H0.
For conciseness, it is possible to define an alias of :mquote: that uses a fixed prefix. Notice how the second example overrides the .g part of the prefix, too.
The proof above had two cases: Heq: n = 0 and Heq: n = S n0. The second goal below is named gg. The last case of the proof below has two induction hypotheses: List.In a l -> List.In a l' and List.In a l' -> List.In a l''. The two permutation hypotheses are H and H0.
Newlines in quoted objects are removed, and line breaks are allowed:
Record P {A B: Type} := { p_first: A; p_second: B }.forall (A B : Type) (p : P), let a_first := p_first p in let b_second := p_second p in {| p_first := a_first; p_second := b_second |} = pdestruct p; reflexivity. Qed.forall (A B : Type) (p : P), let a_first := p_first p in let b_second := p_second p in {| p_first := a_first; p_second := b_second |} = pThe first sentence is Record P {A B: Type} := { p_first: A; p_second: B }.. [1]
[1] The second sentence is Goal forall {A B} (p: @P A B), let a_first := p.(p_first) in let b_second := p.(p_second) in {| p_first := a_first; p_second := b_second |} = p.. Later on we'll see a message: Ignoring implicit binder declaration in unexpected position. [unexpected-implicit-declaration,syntax].
To preserve newlines, use the .. mquote:: directive instead:
Goal forall {A B} (p: @P A B), let a_first := p.(p_first) in let b_second := p.(p_second) in {| p_first := a_first; p_second := b_second |} = p.Ignoring implicit binder declaration in unexpected position. [unexpected-implicit-declaration,syntax]
There, too, you may want to define aliases:
H: Permutation l l'
H0: Permutation l' l''
Finally, you may chose a different Pygments lexer to highlight a quote. For example, here is a piece of Scheme code produced by Extraction:
(define add (lambdas (n m) (match n ((O) m) ((S p) `(S ,(@ add p m))))))
References can also be used to customize the display of goals and hypotheses. In the following, hypotheses whose name start with l are omitted, and so are hypotheses named a and A. After the call to induction (12) the output is further limited to just goals 2 and 4, by excluding all goals and re-including only 2 and 4. In goal 4, hypotheses whose type is exactly list A are shown, regardless of previous status, so l, l', l'' are visible (13). Finally, the -.s(…).msg(…) annotation reduces output for the second line (Check …) to include only the warning that it produces (and not its regular output); and the -.s{Proof.} annotation completely hides the Proof. line.
Permutation l l' -> List.In a l -> List.In a l'all: simpl in *; tauto. Qed.H: Permutation l l'
IHPermutation: List.In a l -> List.In a l'
Hin: List.In a (x :: l)
ggList.In a (x :: l')l, l', l'': list A13
H: Permutation l l'
H0: Permutation l' l''
IHPermutation1: List.In a l -> List.In a l'
IHPermutation2: List.In a l' -> List.In a l''
Hin: List.In a lList.In a l''
A constant concern when displaying proof states to readers is that what is displayed to the user may go stale. Alectryon mitigates the issue by automatically collecting proof states, but simply recording the prover's output doesn't fully solve the issue. That is because the output of a command may change in an unexpected way, without raising an error. For example, we may have written, with an early version of Coq:
To show the recursive definition of addition. But as Coq's standard library got reorganized, the definition of plus
changed to being an alias for Nat.add
, making the output of Print plus
uninteresting. In general, the recommended way to prevent this issue is by recording and versioning the prover's output using Alectryon caching facility (--cache-directory). For small checks, however, Alectryon provides the massert directive, which checks that all references in its body resolve to a part of Coq's output. For example, the following checks that plus
is indeed an alias and Nat.add
a Fixpoint
.