comparison src/Large.v @ 433:5eebaaa9f952

Pass through Large, to incorporate new coqdoc features
author Adam Chlipala <adam@chlipala.net>
date Thu, 26 Jul 2012 16:22:57 -0400
parents 5f25705a10ea
children a54a4a2ea6e4
comparison
equal deleted inserted replaced
432:17d01e51256c 433:5eebaaa9f952
18 18
19 (** %\part{The Big Picture} 19 (** %\part{The Big Picture}
20 20
21 \chapter{Proving in the Large}% *) 21 \chapter{Proving in the Large}% *)
22 22
23 (** It is somewhat unfortunate that the term %``%#"#theorem proving#"#%''% looks so much like the word %``%#"#theory.#"#%''% Most researchers and practitioners in software assume that mechanized theorem proving is profoundly impractical. Indeed, until recently, most advances in theorem proving for higher-order logics have been largely theoretical. However, starting around the beginning of the 21st century, there was a surge in the use of proof assistants in serious verification efforts. That line of work is still quite new, but I believe it is not too soon to distill some lessons on how to work effectively with large formal proofs. 23 (** It is somewhat unfortunate that the term "theorem proving" looks so much like the word "theory." Most researchers and practitioners in software assume that mechanized theorem proving is profoundly impractical. Indeed, until recently, most advances in theorem proving for higher-order logics have been largely theoretical. However, starting around the beginning of the 21st century, there was a surge in the use of proof assistants in serious verification efforts. That line of work is still quite new, but I believe it is not too soon to distill some lessons on how to work effectively with large formal proofs.
24 24
25 Thus, this chapter gives some tips for structuring and maintaining large Coq developments. *) 25 Thus, this chapter gives some tips for structuring and maintaining large Coq developments. *)
26 26
27 27
28 (** * Ltac Anti-Patterns *) 28 (** * Ltac Anti-Patterns *)
29 29
30 (** In this book, I have been following an unusual style, where proofs are not considered finished until they are %\index{fully automated proofs}``%#"#fully automated,#"#%''% in a certain sense. Each such theorem is proved by a single tactic. Since Ltac is a Turing-complete programming language, it is not hard to squeeze arbitrary heuristics into single tactics, using operators like the semicolon to combine steps. In contrast, most Ltac proofs %``%#"#in the wild#"#%''% consist of many steps, performed by individual tactics followed by periods. Is it really worth drawing a distinction between proof steps terminated by semicolons and steps terminated by periods? 30 (** In this book, I have been following an unusual style, where proofs are not considered finished until they are %\index{fully automated proofs}%"fully automated," in a certain sense. Each such theorem is proved by a single tactic. Since Ltac is a Turing-complete programming language, it is not hard to squeeze arbitrary heuristics into single tactics, using operators like the semicolon to combine steps. In contrast, most Ltac proofs "in the wild" consist of many steps, performed by individual tactics followed by periods. Is it really worth drawing a distinction between proof steps terminated by semicolons and steps terminated by periods?
31 31
32 I argue that this is, in fact, a very important distinction, with serious consequences for a majority of important verification domains. The more uninteresting drudge work a proof domain involves, the more important it is to work to prove theorems with single tactics. From an automation standpoint, single-tactic proofs can be extremely effective, and automation becomes more and more critical as proofs are populated by more uninteresting detail. In this section, I will give some examples of the consequences of more common proof styles. 32 I argue that this is, in fact, a very important distinction, with serious consequences for a majority of important verification domains. The more uninteresting drudge work a proof domain involves, the more important it is to work to prove theorems with single tactics. From an automation standpoint, single-tactic proofs can be extremely effective, and automation becomes more and more critical as proofs are populated by more uninteresting detail. In this section, I will give some examples of the consequences of more common proof styles.
33 33
34 As a running example, consider a basic language of arithmetic expressions, an interpreter for it, and a transformation that scales up every constant in an expression. *) 34 As a running example, consider a basic language of arithmetic expressions, an interpreter for it, and a transformation that scales up every constant in an expression. *)
35 35
133 133
134 Abort. 134 Abort.
135 135
136 (** Can you spot what went wrong, without stepping through the script step-by-step? The problem is that [trivial] never fails. Originally, [trivial] had been succeeding in proving an equality that follows by reflexivity. Our change to [times] leads to a case where that equality is no longer true. The invocation [trivial] happily leaves the false equality in place, and we continue on to the span of tactics intended for the second inductive case. Unfortunately, those tactics end up being applied to the _first_ case instead. 136 (** Can you spot what went wrong, without stepping through the script step-by-step? The problem is that [trivial] never fails. Originally, [trivial] had been succeeding in proving an equality that follows by reflexivity. Our change to [times] leads to a case where that equality is no longer true. The invocation [trivial] happily leaves the false equality in place, and we continue on to the span of tactics intended for the second inductive case. Unfortunately, those tactics end up being applied to the _first_ case instead.
137 137
138 The problem with [trivial] could be %``%#"#solved#"#%''% by writing, e.g., [solve [ trivial ]] instead, so that an error is signaled early on if something unexpected happens. However, the root problem is that the syntax of a tactic invocation does not imply how many subgoals it produces. Much more confusing instances of this problem are possible. For example, if a lemma [L] is modified to take an extra hypothesis, then uses of [apply L] will generate more subgoals than before. Old unstructured proof scripts will become hopelessly jumbled, with tactics applied to inappropriate subgoals. Because of the lack of structure, there is usually relatively little to be gleaned from knowledge of the precise point in a proof script where an error is raised. *) 138 The problem with [trivial] could be "solved" by writing, e.g., [solve [ trivial ]] instead, so that an error is signaled early on if something unexpected happens. However, the root problem is that the syntax of a tactic invocation does not imply how many subgoals it produces. Much more confusing instances of this problem are possible. For example, if a lemma [L] is modified to take an extra hypothesis, then uses of [apply L] will generate more subgoals than before. Old unstructured proof scripts will become hopelessly jumbled, with tactics applied to inappropriate subgoals. Because of the lack of structure, there is usually relatively little to be gleaned from knowledge of the precise point in a proof script where an error is raised. *)
139 139
140 Reset times. 140 Reset times.
141 141
142 Fixpoint times (k : nat) (e : exp) : exp := 142 Fixpoint times (k : nat) (e : exp) : exp :=
143 match e with 143 match e with
256 IHe2 : eval e3 * eval e4 = eval e2 256 IHe2 : eval e3 * eval e4 = eval e2
257 ============================ 257 ============================
258 eval e1 * eval e3 * eval e4 = eval e1 * eval e2 258 eval e1 * eval e3 * eval e4 = eval e1 * eval e2
259 ]] 259 ]]
260 260
261 [crush] does not know how to finish this goal. We could finish the proof manually. *) 261 The [crush] tactic does not know how to finish this goal. We could finish the proof manually. *)
262 262
263 rewrite <- IHe2; crush. 263 rewrite <- IHe2; crush.
264 264
265 (** However, the proof would be easier to understand and maintain if we separated this insight into a separate lemma. *) 265 (** However, the proof would be easier to understand and maintain if we separated this insight into a separate lemma. *)
266 266
455 455
456 (** The final subgoal is for multiplication, and it is here that we get stuck in the proof state summarized above. *) 456 (** The final subgoal is for multiplication, and it is here that we get stuck in the proof state summarized above. *)
457 457
458 t. 458 t.
459 459
460 (** What is [t] doing to get us to this point? The %\index{tactics!info}%[info] command can help us answer this kind of question. *) 460 (** What is [t] doing to get us to this point? The %\index{tactics!info}%[info] command can help us answer this kind of question. (As of this writing, [info] is no longer functioning in the most recent Coq release, but I hope it returns.) *)
461 461
462 Undo. 462 Undo.
463 info t. 463 info t.
464
465 (* begin hide *)
466 Definition eir := eq_ind_r.
467 (* end hide *)
464 468
465 (** %\vspace{-.15in}%[[ 469 (** %\vspace{-.15in}%[[
466 == simpl in *; intuition; subst; autorewrite with core in *; 470 == simpl in *; intuition; subst; autorewrite with core in *;
467 simpl in *; intuition; subst; autorewrite with core in *; 471 simpl in *; intuition; subst; autorewrite with core in *;
468 simpl in *; intuition; subst; destruct (reassoc e2). 472 simpl in *; intuition; subst; destruct (reassoc e2).
538 542
539 (** We prove a simple lemma very quickly, using the %\index{Vernacular commands!Time}%[Time] command to measure exactly how quickly. *) 543 (** We prove a simple lemma very quickly, using the %\index{Vernacular commands!Time}%[Time] command to measure exactly how quickly. *)
540 544
541 Lemma slow : forall x y, P x y -> Q x y -> S x y -> f x = f y. 545 Lemma slow : forall x y, P x y -> Q x y -> S x y -> f x = f y.
542 Time eauto 6. 546 Time eauto 6.
543 (** %\vspace{-.2in}%[[ 547 (** <<
544 Finished transaction in 0. secs (0.068004u,0.s) 548 Finished transaction in 0. secs (0.068004u,0.s)
545 ]] 549 >>
546 *) 550 *)
547 551
548 Qed. 552 Qed.
549 553
550 (** Now we add a different hypothesis, which is innocent enough; in fact, it is even provable as a theorem. *) 554 (** Now we add a different hypothesis, which is innocent enough; in fact, it is even provable as a theorem. *)
551 555
552 Hypothesis H3 : forall x y, x = y -> f x = f y. 556 Hypothesis H3 : forall x y, x = y -> f x = f y.
553 557
554 Lemma slow' : forall x y, P x y -> Q x y -> S x y -> f x = f y. 558 Lemma slow' : forall x y, P x y -> Q x y -> S x y -> f x = f y.
555 Time eauto 6. 559 Time eauto 6.
556 (** %\vspace{-.2in}%[[ 560 (** <<
557 Finished transaction in 2. secs (1.264079u,0.s) 561 Finished transaction in 2. secs (1.264079u,0.s)
558 ]] 562 >>
559 563
560 Why has the search time gone up so much? The [info] command is not much help, since it only shows the result of search, not all of the paths that turned out to be worthless. *) 564 Why has the search time gone up so much? The [info] command is not much help, since it only shows the result of search, not all of the paths that turned out to be worthless. *)
561 565
562 (* begin thide *) 566 (* begin thide *)
563 Restart. 567 Restart.
583 587
584 This output does not tell us why proof search takes so long, but it does provide a clue that would be useful if we had forgotten that we added transitivity as a hint. The [eauto] tactic is applying depth-first search, and the proof script where the real action is ends up buried inside a chain of pointless invocations of transitivity, where each invocation uses reflexivity to discharge one subgoal. Each increment to the depth argument to [eauto] adds another silly use of transitivity. This wasted proof effort only adds linear time overhead, as long as proof search never makes false steps. No false steps were made before we added the new hypothesis, but somehow the addition made possible a new faulty path. To understand which paths we enabled, we can use the %\index{tactics!debug}%[debug] command. *) 588 This output does not tell us why proof search takes so long, but it does provide a clue that would be useful if we had forgotten that we added transitivity as a hint. The [eauto] tactic is applying depth-first search, and the proof script where the real action is ends up buried inside a chain of pointless invocations of transitivity, where each invocation uses reflexivity to discharge one subgoal. Each increment to the depth argument to [eauto] adds another silly use of transitivity. This wasted proof effort only adds linear time overhead, as long as proof search never makes false steps. No false steps were made before we added the new hypothesis, but somehow the addition made possible a new faulty path. To understand which paths we enabled, we can use the %\index{tactics!debug}%[debug] command. *)
585 589
586 Restart. 590 Restart.
587 debug eauto 6. 591 debug eauto 6.
592
593 (* begin hide *)
594 Definition deeeebug := (@eq_refl, @sym_eq).
595 (* end hide *)
588 596
589 (** The output is a large proof tree. The beginning of the tree is enough to reveal what is happening: 597 (** The output is a large proof tree. The beginning of the tree is enough to reveal what is happening:
590 [[ 598 [[
591 1 depth=6 599 1 depth=6
592 1.1 depth=6 intro 600 1.1 depth=6 intro
624 632
625 (** As aggravating as the above situation may be, there is greater aggravation to be had from importing library modules with commands like %\index{Vernacular commands!Require Import}%[Require Import]. Such a command imports not just the Gallina terms from a module, but also all the hints for [auto], [eauto], and [autorewrite]. Some very recent versions of Coq include mechanisms for removing hints from databases, but the proper solution is to be very conservative in exporting hints from modules. Consider putting hints in named databases, so that they may be used only when called upon explicitly, as demonstrated in Chapter 13. 633 (** As aggravating as the above situation may be, there is greater aggravation to be had from importing library modules with commands like %\index{Vernacular commands!Require Import}%[Require Import]. Such a command imports not just the Gallina terms from a module, but also all the hints for [auto], [eauto], and [autorewrite]. Some very recent versions of Coq include mechanisms for removing hints from databases, but the proper solution is to be very conservative in exporting hints from modules. Consider putting hints in named databases, so that they may be used only when called upon explicitly, as demonstrated in Chapter 13.
626 634
627 It is also easy to end up with a proof script that uses too much memory. As tactics run, they avoid generating proof terms, since serious proof search will consider many possible avenues, and we do not want to build proof terms for subproofs that end up unused. Instead, tactic execution maintains%\index{thunks}% _thunks_ (suspended computations, represented with closures), such that a tactic's proof-producing thunk is only executed when we run %\index{Vernacular commands!Qed}%[Qed]. These thunks can use up large amounts of space, such that a proof script exhausts available memory, even when we know that we could have used much less memory by forcing some thunks earlier. 635 It is also easy to end up with a proof script that uses too much memory. As tactics run, they avoid generating proof terms, since serious proof search will consider many possible avenues, and we do not want to build proof terms for subproofs that end up unused. Instead, tactic execution maintains%\index{thunks}% _thunks_ (suspended computations, represented with closures), such that a tactic's proof-producing thunk is only executed when we run %\index{Vernacular commands!Qed}%[Qed]. These thunks can use up large amounts of space, such that a proof script exhausts available memory, even when we know that we could have used much less memory by forcing some thunks earlier.
628 636
629 The %\index{tactics!abstract}%[abstract] tactical helps us force thunks by proving some subgoals as their own lemmas. For instance, a proof [induction x; crush] can in many cases be made to use significantly less peak memory by changing it to [induction x; abstract crush]. The main limitation of [abstract] is that it can only be applied to subgoals that are proved completely, with no undetermined unification variables remaining. Still, many large automated proofs can realize vast memory savings via [abstract]. *) 637 The %\index{tactics!abstract}%[abstract] tactical helps us force thunks by proving some subgoals as their own lemmas. For instance, a proof [induction x; crush] can in many cases be made to use significantly less peak memory by changing it to [induction x; abstract crush]. The main limitation of [abstract] is that it can only be applied to subgoals that are proved completely, with no undetermined unification variables in their initial states. Still, many large automated proofs can realize vast memory savings via [abstract]. *)
630 638
631 639
632 (** * Modules *) 640 (** * Modules *)
633 641
634 (** Last chapter's examples of proof by reflection demonstrate opportunities for implementing abstract proof strategies with stronger formal guarantees than can be had with Ltac scripting. Coq's _module system_ provides another tool for more rigorous development of generic theorems. This feature is inspired by the module systems found in Standard ML%~\cite{modules}% and Objective Caml, and the discussion that follows assumes familiarity with the basics of one of those systems. 642 (** Last chapter's examples of proof by reflection demonstrate opportunities for implementing abstract proof strategies with stronger formal guarantees than can be had with Ltac scripting. Coq's _module system_ provides another tool for more rigorous development of generic theorems. This feature is inspired by the module systems found in Standard ML%~\cite{modules}% and Objective Caml, and the discussion that follows assumes familiarity with the basics of one of those systems.
744 (** As in ML, the module system provides an effective way to structure large developments. Unlike in ML, Coq modules add no expressiveness; we can implement any module as an inhabitant of a dependent record type. It is the second-class nature of modules that makes them easier to use than dependent records in many case. Because modules may only be used in quite restricted ways, it is easier to support convenient module coding through special commands and editing modes, as the above example demonstrates. An isomorphic implementation with records would have suffered from lack of such conveniences as module subtyping and importation of the fields of a module. On the other hand, all module values must be determined statically, so modules may not be computed, e.g., within the defintions of normal functions, based on particular function parameters. *) 752 (** As in ML, the module system provides an effective way to structure large developments. Unlike in ML, Coq modules add no expressiveness; we can implement any module as an inhabitant of a dependent record type. It is the second-class nature of modules that makes them easier to use than dependent records in many case. Because modules may only be used in quite restricted ways, it is easier to support convenient module coding through special commands and editing modes, as the above example demonstrates. An isomorphic implementation with records would have suffered from lack of such conveniences as module subtyping and importation of the fields of a module. On the other hand, all module values must be determined statically, so modules may not be computed, e.g., within the defintions of normal functions, based on particular function parameters. *)
745 753
746 754
747 (** * Build Processes *) 755 (** * Build Processes *)
748 756
757 (* begin hide *)
758 Module Lib.
759 Module A.
760 End A.
761 Module B.
762 End B.
763 Module C.
764 End C.
765 End Lib.
766 Module Client.
767 Module D.
768 End D.
769 Module E.
770 End E.
771 End Client.
772 (* end hide *)
773
749 (** As in software development, large Coq projects are much more manageable when split across multiple files and when decomposed into libraries. Coq and Proof General provide very good support for these activities. 774 (** As in software development, large Coq projects are much more manageable when split across multiple files and when decomposed into libraries. Coq and Proof General provide very good support for these activities.
750 775
751 Consider a library that we will name [Lib], housed in directory %\texttt{%#<tt>#LIB#</tt>#%}% and split between files %\texttt{%#<tt>#A.v#</tt>#%}%, %\texttt{%#<tt>#B.v#</tt>#%}%, and %\texttt{%#<tt>#C.v#</tt>#%}%. A simple %\index{Makefile}%Makefile will compile the library, relying on the standard Coq tool %\index{coq\_makefile}\texttt{%#<tt>#coq_makefile#</tt>#%}% to do the hard work. 776 Consider a library that we will name [Lib], housed in directory <<LIB>> and split between files <<A.v>>, <<B.v>>, and <<C.v>>. A simple %\index{Makefile}%Makefile will compile the library, relying on the standard Coq tool %\index{coq\_makefile}\texttt{%#<tt>#coq_makefile#</tt>#%}% to do the hard work.
752 777
753 << 778 <<
754 MODULES := A B C 779 MODULES := A B C
755 VS := $(MODULES:%=%.v) 780 VS := $(MODULES:%=%.v)
756 781
765 clean:: Makefile.coq 790 clean:: Makefile.coq
766 $(MAKE) -f Makefile.coq clean 791 $(MAKE) -f Makefile.coq clean
767 rm -f Makefile.coq 792 rm -f Makefile.coq
768 >> 793 >>
769 794
770 The Makefile begins by defining a variable %\texttt{%#<tt>#VS#</tt>#%}% holding the list of filenames to be included in the project. The primary target is %\texttt{%#<tt>#coq#</tt>#%}%, which depends on the construction of an auxiliary Makefile called %\texttt{%#<tt>#Makefile.coq#</tt>#%}%. Another rule explains how to build that file. We call %\texttt{%#<tt>#coq_makefile#</tt>#%}%, using the %\texttt{%#<tt>#-R#</tt>#%}% flag to specify that files in the current directory should be considered to belong to the library [Lib]. This Makefile will build a compiled version of each module, such that %\texttt{%#<tt>#X.v#</tt>#%}% is compiled into %\texttt{%#<tt>#X.vo#</tt>#%}%. 795 The Makefile begins by defining a variable <<VS>> holding the list of filenames to be included in the project. The primary target is <<coq>>, which depends on the construction of an auxiliary Makefile called <<Makefile.coq>>. Another rule explains how to build that file. We call %\texttt{%#<tt>#coq_makefile#</tt>#%}%, using the <<-R>> flag to specify that files in the current directory should be considered to belong to the library [Lib]. This Makefile will build a compiled version of each module, such that <<X.v>> is compiled into <<X.vo>>.
771 796
772 Now code in %\texttt{%#<tt>#B.v#</tt>#%}% may refer to definitions in %\texttt{%#<tt>#A.v#</tt>#%}% after running 797 Now code in <<B.v>> may refer to definitions in <<A.v>> after running
773 [[ 798 [[
774 Require Import Lib.A. 799 Require Import Lib.A.
775 ]] 800 ]]
776 %\vspace{-.15in}%Library [Lib] is presented as a module, containing a submodule [A], which contains the definitions from %\texttt{%#<tt>#A.v#</tt>#%}%. These are genuine modules in the sense of Coq's module system, and they may be passed to functors and so on. 801 %\vspace{-.15in}%Library [Lib] is presented as a module, containing a submodule [A], which contains the definitions from <<A.v>>. These are genuine modules in the sense of Coq's module system, and they may be passed to functors and so on.
777 802
778 The command [Require Import] is a convenient combination of two more primitive commands. The %\index{Vernacular commands!Require}%[Require] command finds the %\texttt{%#<tt>#.vo#</tt>#%}% file containing the named module, ensuring that the module is loaded into memory. The %\index{Vernacular commands!Import}%[Import] command loads all top-level definitions of the named module into the current namespace, and it may be used with local modules that do not have corresponding %\texttt{%#<tt>#.vo#</tt>#%}% files. Another command, %\index{Vernacular commands!Load}%[Load], is for inserting the contents of a named file verbatim. It is generally better to use the module-based commands, since they avoid rerunning proof scripts, and they facilitate reorganization of directory structure without the need to change code. 803 The command [Require Import] is a convenient combination of two more primitive commands. The %\index{Vernacular commands!Require}%[Require] command finds the <<.vo>> file containing the named module, ensuring that the module is loaded into memory. The %\index{Vernacular commands!Import}%[Import] command loads all top-level definitions of the named module into the current namespace, and it may be used with local modules that do not have corresponding <<.vo>> files. Another command, %\index{Vernacular commands!Load}%[Load], is for inserting the contents of a named file verbatim. It is generally better to use the module-based commands, since they avoid rerunning proof scripts, and they facilitate reorganization of directory structure without the need to change code.
779 804
780 Now we would like to use our library from a different development, called [Client] and found in directory %\texttt{%#<tt>#CLIENT#</tt>#%}%, which has its own Makefile. 805 Now we would like to use our library from a different development, called [Client] and found in directory <<CLIENT>>, which has its own Makefile.
781 806
782 << 807 <<
783 MODULES := D E 808 MODULES := D E
784 VS := $(MODULES:%=%.v) 809 VS := $(MODULES:%=%.v)
785 810
794 clean:: Makefile.coq 819 clean:: Makefile.coq
795 $(MAKE) -f Makefile.coq clean 820 $(MAKE) -f Makefile.coq clean
796 rm -f Makefile.coq 821 rm -f Makefile.coq
797 >> 822 >>
798 823
799 We change the %\texttt{%#<tt>#coq_makefile#</tt>#%}% call to indicate where the library [Lib] is found. Now %\texttt{%#<tt>#D.v#</tt>#%}% and %\texttt{%#<tt>#E.v#</tt>#%}% can refer to definitions from [Lib] module [A] after running 824 We change the %\texttt{%#<tt>#coq_makefile#</tt>#%}% call to indicate where the library [Lib] is found. Now <<D.v>> and <<E.v>> can refer to definitions from [Lib] module [A] after running
800 [[ 825 [[
801 Require Import Lib.A. 826 Require Import Lib.A.
802 ]] 827 ]]
803 %\vspace{-.15in}\noindent{}%and %\texttt{%#<tt>#E.v#</tt>#%}% can refer to definitions from %\texttt{%#<tt>#D.v#</tt>#%}% by running 828 %\vspace{-.15in}\noindent{}%and <<E.v>> can refer to definitions from <<D.v>> by running
804 [[ 829 [[
805 Require Import Client.D. 830 Require Import Client.D.
806 ]] 831 ]]
807 %\vspace{-.15in}%It can be useful to split a library into several files, but it is also inconvenient for client code to import library modules individually. We can get the best of both worlds by, for example, adding an extra source file %\texttt{%#<tt>#Lib.v#</tt>#%}% to [Lib]'s directory and Makefile, where that file contains just this line:%\index{Vernacular commands!Require Export}% 832 %\vspace{-.15in}%It can be useful to split a library into several files, but it is also inconvenient for client code to import library modules individually. We can get the best of both worlds by, for example, adding an extra source file <<Lib.v>> to [Lib]'s directory and Makefile, where that file contains just this line:%\index{Vernacular commands!Require Export}%
808 [[ 833 [[
809 Require Export Lib.A Lib.B Lib.C. 834 Require Export Lib.A Lib.B Lib.C.
810 ]] 835 ]]
811 %\vspace{-.15in}%Now client code can import all definitions from all of [Lib]'s modules simply by running 836 %\vspace{-.15in}%Now client code can import all definitions from all of [Lib]'s modules simply by running
812 [[ 837 [[
814 ]] 839 ]]
815 %\vspace{-.15in}%The two Makefiles above share a lot of code, so, in practice, it is useful to define a common Makefile that is included by multiple library-specific Makefiles. 840 %\vspace{-.15in}%The two Makefiles above share a lot of code, so, in practice, it is useful to define a common Makefile that is included by multiple library-specific Makefiles.
816 841
817 %\medskip% 842 %\medskip%
818 843
819 The remaining ingredient is the proper way of editing library code files in Proof General. Recall this snippet of %\texttt{%#<tt>#.emacs#</tt>#%}% code from Chapter 2, which tells Proof General where to find the library associated with this book. 844 The remaining ingredient is the proper way of editing library code files in Proof General. Recall this snippet of <<.emacs>> code from Chapter 2, which tells Proof General where to find the library associated with this book.
820 845
821 << 846 <<
822 (custom-set-variables 847 (custom-set-variables
823 ... 848 ...
824 '(coq-prog-args '("-I" "/path/to/cpdt/src")) 849 '(coq-prog-args '("-I" "/path/to/cpdt/src"))
835 '(coq-prog-args '("-R" "LIB" "Lib" "-R" "CLIENT" "Client")) 860 '(coq-prog-args '("-R" "LIB" "Lib" "-R" "CLIENT" "Client"))
836 ... 861 ...
837 ) 862 )
838 >> 863 >>
839 864
840 When working on multiple projects, it is useful to leave multiple versions of this setting in your %\texttt{%#<tt>#.emacs#</tt>#%}% file, commenting out all but one of them at any moment in time. To switch between projects, change the commenting structure and restart Emacs. 865 When working on multiple projects, it is useful to leave multiple versions of this setting in your <<.emacs>> file, commenting out all but one of them at any moment in time. To switch between projects, change the commenting structure and restart Emacs.
841 866
842 Alternatively, we can revisit the directory-local settings approach and write the following into a file %\texttt{%#<tt>#.dir-locals.el#</tt>#%}% in %\texttt{%#<tt>#CLIENT#</tt>#%}%: 867 Alternatively, we can revisit the directory-local settings approach and write the following into a file <<.dir-locals.el>> in <<CLIENT>>:
843 868
844 << 869 <<
845 ((coq-mode . ((coq-prog-args . 870 ((coq-mode . ((coq-prog-args .
846 ("-emacs-U" "-R" "LIB" "Lib" "-R" "CLIENT" "Client"))))) 871 ("-emacs-U" "-R" "LIB" "Lib" "-R" "CLIENT" "Client")))))
847 >> 872 >>