├── README.md ├── Rakefile ├── doc ├── discussion.lagda ├── extensible.lagda ├── intro.lagda ├── llncs.cls ├── main.bib ├── main.fmt ├── main.lagda ├── motivation.lagda ├── old_main.lagda ├── preamble.tex ├── prolog.lagda ├── reflection.lagda ├── sigplanconf.cls ├── textgreek.sty ├── todo.md └── typeclasses.lagda └── src ├── Auto.agda ├── Auto ├── Core.agda ├── Counting.agda ├── Example │ ├── Even.agda │ ├── Sublists.agda │ └── TypeClasses.agda └── Extensible.agda ├── LICENSE ├── ProofSearch.agda └── Unification.agda /README.md: -------------------------------------------------------------------------------- 1 | ## Auto In Agda 2 | 3 | #### Abstract 4 | 5 | > As proofs in type theory become increasingly complex, there is a 6 | > growing need to provide better proof automation. This paper shows 7 | > how to implement a Prolog-style resolution procedure in the 8 | > dependently typed programming language Agda. Connecting this 9 | > resolution procedure to Agda's reflection mechanism provides a 10 | > first-class proof search tactic for first-order Agda 11 | > terms. Furthermore, the same mechanism may be used in tandem with 12 | > Agda's instance arguments to implement type classes in the style of 13 | > Haskell. As a result, writing proof automation tactics need not be 14 | > different from writing any other program. 15 | 16 | #### Technical Details 17 | 18 | This repository contains the sources for the 19 | *[Auto In Agda](https://wenkokke.github.io/pubs/mpc2015.pdf)*. 20 | The `paper` sub-directory contains the literate Agda files that make up the paper. 21 | The `code` sub-directory contains the Agda source code. 22 | 23 | Some notes: 24 | 25 | - the code was written for use with Agda 2.4.2 and [version 0.8.1 of the Agda standard 26 | library](https://github.com/agda/agda-stdlib/releases/tag/v0.8.1); 27 | 28 | - the code is released under an [MIT license](code/LICENSE); 29 | 30 | - the paper is compiled using [lhs2TeX](http://www.andres-loeh.de/lhs2tex/); 31 | 32 | - compilation of the paper is facilitated using a 33 | [rake](http://rake.rubyforge.org/) build script (call `rake paper`). 34 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/clean' 2 | 3 | 4 | SrcDir = 'src' 5 | DocDir = 'doc' 6 | DocFiles = FileList["#{DocDir}/*.lagda", 7 | "#{DocDir}/*.fmt"] 8 | TeXFiles = FileList["#{DocDir}/main.tex", 9 | "#{DocDir}/main.bib", 10 | "#{DocDir}/preamble.tex" ] 11 | 12 | 13 | desc "Compile and open the paper" 14 | task :default => :build do 15 | system "open #{DocDir}/main.pdf" 16 | end 17 | 18 | 19 | desc "Compile the paper" 20 | task :build => "#{DocDir}/main.pdf" 21 | 22 | 23 | desc "Compile the paper" 24 | file "#{DocDir}/main.pdf" => TeXFiles do 25 | Dir.chdir(DocDir) do 26 | 27 | system "pdflatex main.tex" 28 | if $?.success? 29 | system "bibtex main" 30 | if $?.success? 31 | system "pdflatex main.tex" 32 | system "pdflatex main.tex" 33 | end 34 | end 35 | 36 | end 37 | end 38 | 39 | 40 | desc "Compile literate Agda to TeX (and remove implicits)" 41 | file "#{DocDir}/main.tex" => DocFiles do |t| 42 | 43 | f_abs = File.absolute_path(t.name) 44 | f_lagda = f_abs.ext('.lagda') 45 | f_tex = f_abs.ext('.tex') 46 | f_dir = File.dirname(f_abs) 47 | 48 | Dir.chdir(f_dir) do 49 | 50 | cmd = "lhs2TeX --agda #{ f_lagda } -o #{ f_tex }" 51 | puts cmd 52 | system cmd 53 | 54 | fail "error in lhs2TeX" unless $?.success? 55 | 56 | end 57 | end 58 | 59 | 60 | TempDocPats = ['*.log','*.ptb','*.blg','*.bbl','*.aux','*.snm', 61 | '*.toc','*.nav','*.out','auto','main.tex'] 62 | TempDocFiles = FileList.new(TempDocPats.map {|fn| File.join(DocDir,fn) }) 63 | TempSrcPats = ['*.agdai'] 64 | TempSrcFiles = FileList.new(TempSrcPats.map { |fn| File.join(SrcDir,fn) }) 65 | 66 | CLEAN.include(TempDocFiles,TempSrcFiles) 67 | CLOBBER.include('#{DocDir}/main.pdf') 68 | -------------------------------------------------------------------------------- /doc/discussion.lagda: -------------------------------------------------------------------------------- 1 | \section{Discussion} 2 | \label{sec:discussion} 3 | 4 | The |auto| function presented here is far from perfect. This section 5 | not only discusses its limitations, but compares it to existing proof 6 | automation techniques in interactive proof assistants. 7 | 8 | %% REASON: 9 | %% I removed the section on performance here, since we can and 10 | %% should tone it down a whole bunch. My rewrites and possibly Agda's 11 | %% new treatment of natural numbers as Haskell integers (if addition is 12 | %% treated as a Haskell operation) sped our tactic up a WHOLE bunch. 13 | %% 14 | %%\paragraph{Performance} 15 | %%First of all, the performance of the |auto| function is terrible. Any 16 | %%proofs that require a depth greater than ten are intractable in 17 | %%practice. This is an immediate consequence of Agda's poor compile-time 18 | %%evaluation. The current implementation is call-by-name and does no 19 | %%optimisation whatsoever. While a mature evaluator is beyond the scope 20 | %%of this project, we believe that it is essential for Agda proofs to 21 | %%scale beyond toy examples. Simple optimizations, such as the erasure 22 | %%of the natural number indexes used in unification~\cite{brady-opt}, 23 | %%would certainly help speed up the proof search. 24 | 25 | \paragraph{Restricted language fragment} 26 | The |auto| function can only handle first-order terms. Even though 27 | higher-order unification is not decidable in general, we believe that it 28 | should be possible to adapt our algorithm to work on second-order 29 | goals. 30 | Furthermore, there are plenty of Agda features that are not 31 | supported or ignored by our quotation functions, such as universe 32 | polymorphism, instance arguments, and primitive functions. 33 | 34 | Even for definitions that seem completely first-order, our |auto| 35 | function can fail unexpectedly. Consider the following definition of 36 | the pair type: 37 | \begin{code} 38 | _×_ : (A B : Set) → Set 39 | A × B = Σ A (λ _ → B) 40 | 41 | pair : {A B : Set} -> A -> B -> A × B 42 | pair x y = x , y 43 | \end{code} 44 | Here a (non-dependent) pair is defined as a special case of the 45 | dependent pair type |Σ|. Now consider the following trivial lemma: 46 | \begin{code} 47 | andIntro : (A : Set) -> (B : Set) -> A × B 48 | \end{code} 49 | Somewhat surprisingly, trying to prove this lemma using our |auto| 50 | function, providing the |pair| function as a hint, fails. 51 | The |quoteGoal| construct always returns 52 | the goal in normal form, which exposes the higher-order nature of |A × 53 | B|. Converting the goal |(A × (λ _ → B))| to a |PsTerm| will raise the 54 | `exception' |unsupportedSyntax|; the goal type contains a lambda which 55 | causes the proof search to fail before it has even started. 56 | 57 | % \todo{No longer relevant; tactic also takes context} 58 | % Furthermore, there are some limitations on the hints that may be 59 | % stored in the hint database. At the moment, we construct every hint by 60 | % quoting an Agda |Name|. Not all useful hints, however, have such a 61 | % |Name|, such as any variables locally bound in the context by pattern 62 | % matching or function arguments. For example, the following call to the 63 | % |auto| function fails to produce the desired proof: 64 | % \begin{code} 65 | % trivial : Even n → Even (n + 2) 66 | % trivial e = tactic (auto 5 hints) 67 | % \end{code} 68 | % The variable |e|, necessary to complete the proof is not part of the 69 | % hint database. The |tactic| keyword in the upcoming Agda release 70 | % addresses this, by providing both the current goal and a list of the 71 | % terms bound in the local context as arguments to the tactic functions. 72 | % \review{What if we remove e in the LHS of trivial, and ask the system 73 | % to find a proof for Even n -> Even (n+2)? Also, eliminate the 74 | % newline.} 75 | % \pepijn{Do we want to mention that we can now easily pattern-match, 76 | % rewrite the paper to include the most recent version of |tactic|, 77 | % etc? Or should we just delete this section?} 78 | % Wouter: I've commented out this section. It's hard to be precise here as 79 | % long as Agda is still in flux. 80 | 81 | \paragraph{Refinement} 82 | The |auto| function returns a complete proof term or fails 83 | entirely. This is not always desirable. We may want to return an 84 | incomplete proof, that still has open holes that the user must 85 | complete. The difficulty lies with the current implementation of Agda's 86 | reflection mechanism, as it cannot generate an incomplete |Term|. 87 | 88 | In the future, it may be interesting to explore how to integrate proof 89 | automation using the reflection mechanism better with Agda's IDE. For 90 | instance, we could create an IDE feature which replaces a call to |auto| with 91 | the proof terms that it generates. 92 | As a result, reloading the file 93 | would no longer need to recompute the proof terms. 94 | 95 | \paragraph{Metatheory} 96 | The |auto| function is necessarily untyped because the interface of 97 | Agda's reflection mechanism is untyped. Defining a well-typed 98 | representation of dependent types in a dependently typed language 99 | remains an open problem, despite various efforts in this 100 | direction~\citep{james-phd,nisse,devriese,kipling}. If we had such a 101 | representation, however, we could use the type information to prove 102 | that when the |auto| function succeeds, the resulting term has the 103 | correct type. As it stands, a bug in our |auto| function could 104 | potentially produce an ill-typed proof term, that only causes a type 105 | error when that term is unquoted. 106 | 107 | \paragraph{Variables} 108 | The astute reader will have noticed that the tactic we have 109 | implemented is closer to Coq's |eauto| tactic than the |auto| 110 | tactic. The difference between the two tactics lies in the treatment 111 | of unification variables: |eauto| may introduce new variables during 112 | unification; |auto| will never do so. It would be fairly 113 | straightforward to restrict our tactic to only apply hints when all 114 | variables known. A suitable instantiation algorithm, which we could 115 | use instead of the more general unification algorithm in this paper, 116 | has already been developed in previous work~\citep{vannoort}. 117 | 118 | \paragraph{Technical limitations} 119 | 120 | The |auto| tactic relies on the unification algorithm and proof search 121 | mechanism we have implemented ourselves. These are all run \emph{at 122 | compile time}, using the reflection mechanism to try and find a 123 | suitable proof term. It is very difficult to say anything meaningful 124 | about the performance of the |auto| tactic, as Agda currently has no 125 | mechanism for debugging or profiling programs run at compile time. We 126 | hope that further advancement of the Agda compiler and associated 127 | toolchain can help provide meaningful measurements of the performance 128 | of |auto|. Similarly, a better (static) debugger would be invaluable 129 | when trying to understand why a call to |auto| failed to produce the 130 | desired proof. 131 | 132 | 133 | % Finally, we should mention that a technical limitation in Agda's 134 | % reflection mechanism prevents us from proving recursive theorems using 135 | % the |auto| tactic. Ideally, we would like to 136 | 137 | % As it stands, proving soundness of the 138 | % |auto| function is non-trivial: we would need to define the typing 139 | % rules of Agda's |Term| data type and prove that the |Term| we produce 140 | % witnesses the validity of our goal |Term|. 141 | % It may be slightly easier 142 | % to ignore Agda's reflection mechanism and instead verify the 143 | % metatheory of the Prolog interpreter: if a proof exists at some given 144 | % depth, |dfs| should find it; any |Proof| returned by 145 | % |dfs| should correspond to a valid derivation. 146 | % \review{Somewhere early in the paper you say that working with a typed 147 | % representation of terms would not offer additional safety for the 148 | % proof search procedure. In the Metatheory paragraph in Section 6 you 149 | % seem to contradict that. Anyway, what I would be additionally 150 | % interested in here is to hear about whether a typed representation 151 | % might also help the effectiveness of the tactic. (Since fewer 152 | % potential proof terms would need to be considered?)} 153 | % \pepijn{Nope, because we compute on the types... so typing the 154 | % metatheory wouldn't really allow us to make anything simpler, it 155 | % would just give a richer structure for the `Proof` objects. What I 156 | % mean here is that we could encode the proof structure as a list of 157 | % subgoals, and the partial proof as a function from a heterogeneous 158 | % list whose types are indexed by those subgoals, to a value whose 159 | % type is equal to the top-level goal.} 160 | %% Wouter -- I've commented out part of this discussion. We only put it in there to make 161 | %% an ICFP reviewer happy, if I remember correctly. 162 | 163 | \subsection*{Related work} 164 | There are several other interactive proof assistants, dependently 165 | typed programming languages, and alternative forms of proof 166 | automation in Agda. In the remainder of this section, we will briefly compare 167 | the approach taken in this paper to these existing systems. 168 | 169 | \paragraph{Coq} 170 | Coq has rich support for proof automation. The Ltac language 171 | and the many primitive, customizable tactics are extremely 172 | powerful~\citep{chlipala}. Despite Coq's success, it is still 173 | worthwhile to explore better methods for proof automation. Recent work 174 | on Mtac~\citep{mtac} shows how to add a typed language for proof 175 | automation on top of Ltac. Furthermore, Ltac itself is not designed to 176 | be a general purpose programming language. It can be difficult to 177 | abstract over certain patterns and debugging 178 | proof automation is not easy. The programmable proof automation, 179 | written using reflection, presented here may not be as mature as Coq's 180 | Ltac language, but addresses these issues. 181 | 182 | More recently, \cite{malecha} have designed a higher-order reflective 183 | programming language (MirrorCore) and an associated tactic language 184 | (Rtac). MirrorCore defines a unification algorithm -- similar to the 185 | one we have implemented in this paper. Alternative implementations 186 | of several familiar Coq tactics, such as |eauto| and |setoid_rewrite|, 187 | have been developed using Rtac. The authors have identified several 188 | similar advantages of `programming' tactics, rather than using 189 | built-in primitives, that we mention in this paper, such as 190 | manipulating and assembling first-class hint databases. 191 | 192 | \paragraph{Idris} 193 | The dependently typed programming language Idris also has a collection 194 | of tactics, inspired by some of the more simple Coq tactics, such as 195 | |rewrite|, |intros|, or |exact|. Each of these tactics is built-in and 196 | implemented as part of the Idris system. There is a small Haskell 197 | library for tactic writers to use that exposes common commands, such 198 | as unification, evaluation, or type checking. Furthermore, there are 199 | library functions to help handle the construction of proof terms, 200 | generation of fresh names, and splitting sub-goals. This approach is 201 | reminiscent of the HOL family of theorem provers~\citep{hol} or Coq's 202 | plug-in mechanism. An important drawback is that tactic writers need 203 | to write their tactics in a different language to the rest of their 204 | Idris code; furthermore, any changes to tactics requires a 205 | recompilation of the entire Idris system. 206 | 207 | \paragraph{Agsy} 208 | Agda already has a built-in `auto' tactic that outperforms the |auto| 209 | function we have defined here~\citep{lindblad}. It is nicely integrated 210 | with the IDE and does not require the users to provide an explicit 211 | hint database. It is, however, implemented in Haskell and shipped as 212 | part of the Agda system. As a result, users have very few 213 | opportunities for customization: there is limited control over which 214 | hints may (or may not) be used; there is no way to assign priorities 215 | to certain hints; and there is a single fixed search strategy. In 216 | contrast to the proof search presented here, where we have much more 217 | fine grained control over all these issues. 218 | 219 | \subsection*{Conclusion} 220 | 221 | The proof automation presented in this paper is not as mature as some 222 | of these alternative systems. Yet we strongly believe that this style 223 | of proof automation is worth pursuing further. 224 | 225 | The advantages of using reflection to program proof tactics should be 226 | clear: we do not need to learn a new programming language to write new 227 | tactics; we can use existing language technology to debug and test our 228 | tactics; and we can use all of Agda's expressive power in the design 229 | and implementation of our tactics. If a particular problem domain 230 | requires a different search strategy, this can be implemented by 231 | writing a new traversal over a |SearchTree|. Hint databases are 232 | first-class values. There is never any built-in magic; there are no 233 | compiler primitives beyond Agda's reflection mechanism. 234 | 235 | The central philosophy of Martin-L\"of type theory is that the 236 | construction of programs and proofs is the same activity. Any 237 | external language for proof automation renounces this philosophy. This 238 | paper demonstrates that proof automation is not inherently at odds 239 | with the philosophy of type theory. Paraphrasing 240 | Martin-L\"of~\citeyearpar{martin-lof}, it no longer seems possible to 241 | distinguish the discipline of \emph{programming} from the 242 | \emph{construction} of mathematics. 243 | 244 | % \pepijn{The |auto| tactic currently works under the latest release of 245 | % Agda; however, the changes to |tactic| have not yet been 246 | % released. Therefore, I feel |auto| will probably break soon (for a 247 | % while, at least).} 248 | % Wouter: we need to prepare the best paper we can NOW. We can always update 249 | % the library quite easily when the new version of Agda is released. 250 | 251 | 252 | %%% Local Variables: 253 | %%% mode: latex 254 | %%% TeX-master: t 255 | %%% TeX-command-default: "rake" 256 | %%% End: 257 | -------------------------------------------------------------------------------- /doc/extensible.lagda: -------------------------------------------------------------------------------- 1 | \section{Extensible proof search} 2 | \label{sec:extensible} 3 | 4 | As we promised in the previous section, we will now explore several 5 | variations and extensions to the |auto| tactic described above. 6 | 7 | 8 | \subsection*{Custom search strategies} 9 | 10 | The simplest change we can make is to abstract over the search 11 | strategy used by the |auto| function. In the interest of readability 12 | we will create a simple alias for the types of search strategies. 13 | A |Strategy| represents a function which searches a |SearchTree| up to 14 | |depth|, and returns a list of the leaves (or |Proof|s) found in the 15 | |SearchTree| in an order which is dependent on the search strategy. 16 | \begin{code} 17 | Strategy = (depth : ℕ) → SearchTree A → List A 18 | \end{code} 19 | The changed type of the |auto| function now becomes. 20 | \begin{code} 21 | auto : Strategy → ℕ → HintDB → AgTerm → AgTerm 22 | \end{code} 23 | This will allow us to choose whether to pass in |dfs|, breadth-first 24 | search or even a custom user-provided search strategy. 25 | 26 | \subsection*{Custom hint databases} 27 | 28 | In addition, we have developed a variant of the |auto| tactic 29 | described in the paper that allows users to define their own type of 30 | hint database, provided they can implement the following interface: 31 | \begin{code} 32 | HintDB : Set 33 | Hint : ℕ → Set 34 | getHints : HintDB → Hints 35 | getRule : Hint k → Rule k 36 | getTr : Hint k → (HintDB → HintDB) 37 | \end{code} 38 | Besides the obvious types for hints and rules, we allow hint databases 39 | to evolve during the proof search. The user-defined |getTr| function 40 | describes a transformation that may modify the hint database after a 41 | certain hint has been applied. 42 | 43 | Using this interface, we can implement many variations on proof 44 | search. For instance, we could implement a `linear' proof search 45 | function that removes a rule from the hint database after it has been 46 | applied. Alternatively, we may want to assign priorities to our 47 | hints. To illustrate one possible application of this interface, we 48 | will describe a hint database implementation that limits the usage of 49 | certain rules. Before we do so, however, we need to introduce a 50 | motivating example. 51 | 52 | \subsection*{Example: limited usage of hints} 53 | 54 | We start by defining the following sublist relation, taken from the 55 | Agda tutorial~\citep{agda-tutorial}: 56 | \begin{code} 57 | data _⊆_ : List A → List A → Set where 58 | stop : [] ⊆ [] 59 | drop : xs ⊆ ys → xs ⊆ y ∷ ys 60 | keep : xs ⊆ ys → x ∷ xs ⊆ x ∷ ys 61 | \end{code} 62 | It is easy to show that the sublist relation is both reflexive and 63 | transitive---and using these simple proofs, we can build up a small 64 | hint database to search for proofs on the sublist relation. 65 | \begin{code} 66 | hintdb : HintDB 67 | hintdb = ε << quote drop << quote keep << quote ⊆-refl << quote ⊆-trans 68 | \end{code} 69 | Our |auto| tactic quickly finds a proof for the following lemma: 70 | \begin{code} 71 | lemma₁ : ws ⊆ 1 ∷ xs → xs ⊆ ys → ys ⊆ zs → ws ⊆ 1 ∷ 2 ∷ zs 72 | lemma₁ = tactic (auto dfs 10 hintdb) 73 | \end{code} 74 | The following lemma, however, is false. 75 | \begin{code} 76 | lemma₂ : ws ⊆ 1 ∷ xs → xs ⊆ ys → ys ⊆ zs → ws ⊆ 2 ∷ zs 77 | lemma₂ = tactic (auto dfs 10 hintdb) 78 | \end{code} 79 | Indeed, this example does not type check and our tactic reports that 80 | the search space is exhausted. As noted by \citet{chlipala} when 81 | examining tactics in Coq, |auto| will nonetheless spend a considerable 82 | amount of time trying to construct a proof. As the |trans| rule is 83 | always applicable, the proof search will construct a search tree up to 84 | the full search depth---resulting in an exponental running time. 85 | 86 | We will use a variation of the |auto| tactic to address this 87 | problem. Upon constructing the new hint database, users may assign 88 | limits to the number of times certain hints may be used. By limiting the 89 | usage of transitivity, our tactic will fail more quickly. 90 | 91 | To begin with, we choose the representation of our hints: a pair of a 92 | rule and a `counter' that records how often the rule may still be 93 | applied: 94 | \begin{code} 95 | record Hint (k : ℕ) : Set where 96 | field 97 | rule : Rule k 98 | counter : Counter 99 | \end{code} 100 | These |counter| values will either be a natural number |n|, 101 | representing that the rule can still be used at most |n| 102 | times; or |⊤|, when the usage of the rule is unrestricted. 103 | \begin{code} 104 | Counter : Set 105 | Counter = ℕ ⊎ ⊤ 106 | \end{code} 107 | Next, we define a decrementing function, |decrCounter|, that returns 108 | |nothing| when a rule can no longer be applied: 109 | \begin{code} 110 | decrCounter : Counter → Maybe Counter 111 | decrCounter (inj₁ 0) = nothing 112 | decrCounter (inj₁ 1) = nothing 113 | decrCounter (inj₁ x) = just (inj₁ (pred x)) 114 | decrCounter (inj₂ tt) = just (inj₂ tt) 115 | \end{code} 116 | Given a hint |h|, the transition function will now simply find the 117 | position of |h| in the hint database and decrement the hint's counter, 118 | removing it from the database if necessary. 119 | 120 | We can redefine the default insertion function (|_<<_|) to allow 121 | unrestricted usage of a rule. However, we will define a new insertion 122 | function which will allow the user to limit the usage of a rule during proof search: 123 | \begin{code} 124 | _<<[_]_ : HintDB → ℕ → Name → HintDB 125 | db <<[ 0 ] _ = db 126 | db <<[ x ] n with (name2rule n) 127 | db <<[ x ] n | inj₁ msg = db 128 | db <<[ x ] n | inj₂ (k , r) = db ++ [ k , record { rule = r , counter = inj₁ x } ] 129 | \end{code} 130 | We now revisit our original hint database and limit the number 131 | of times transitivity may be applied: 132 | \begin{code} 133 | hintdb : HintDB 134 | hintdb = ε << quote drop 135 | << quote keep 136 | << quote refl 137 | <<[ 2 ] quote trans 138 | \end{code} 139 | If we were to search for a proof of |lemma₂| now, our proof search 140 | fails sooner. \emph{A fortiori}, if we use this restricted database 141 | when searching for a proof of |lemma₁|, the |auto| function succeeds 142 | sooner, as we have greatly reduced the search space. Of course, there 143 | are plenty of lemmas that require more than two applications of 144 | transitivity. The key insight, however, is that users now have control 145 | over these issues -- something which is not even possible in current 146 | implementations of |auto| in Coq. 147 | 148 | %%% Local Variables: 149 | %%% mode: latex 150 | %%% TeX-master: t 151 | %%% TeX-command-default: "rake" 152 | %%% End: 153 | -------------------------------------------------------------------------------- /doc/intro.lagda: -------------------------------------------------------------------------------- 1 | \section{Introduction} 2 | \label{sec:intro} 3 | 4 | Writing proof terms in type theory is hard and often tedious. 5 | Interactive proof assistants based on type theory, such as 6 | Agda~\citep{agda} or Coq~\citeyearpar{coq}, take very different approaches to 7 | facilitating this process. 8 | 9 | The Coq proof assistant has two distinct language fragments. Besides 10 | the programming language Gallina, there is a separate tactic language 11 | for writing and programming proof scripts. Together with several 12 | highly customizable tactics, the tactic language Ltac can provide 13 | powerful proof automation~\citep{chlipala}. Having to introduce a 14 | separate tactic language, however, seems at odds with the spirit of 15 | type theory, where a single language is used for both proof and 16 | computation. Having a separate language for programming proofs has 17 | its drawbacks: programmers need to learn another language to automate 18 | proofs, debugging Ltac programs can be difficult, and the resulting 19 | proof automation may be inefficient~\citep{brabaint}. 20 | 21 | Agda does not have Coq's segregation of proof and programming 22 | language. Instead, programmers are encouraged to automate proofs by 23 | writing their own solvers~\citep{ulf-tphols}. In combination with 24 | Agda's reflection mechanism~\citep{agda-relnotes-228,van-der-walt}, developers can write 25 | powerful automatic decision procedures~\citep{allais}. Unfortunately, 26 | not all proofs are easily automated in this fashion. If this is the case, 27 | the user is forced to interact with the integrated development 28 | environment and manually construct a proof term step by step. 29 | 30 | This paper tries to combine the best of both worlds by implementing a 31 | library for proof search \emph{within} Agda itself. In other words, we 32 | have defined a \emph{program} for the automatic \emph{construction} of 33 | \emph{mathematical} proofs. More specifically, this paper makes 34 | several novel contributions: 35 | \begin{itemize} 36 | \item % 37 | We show how to implement a Prolog interpreter in the style of 38 | \citet{stutterheim} in Agda (Section~\ref{sec:prolog}). Note that, 39 | in contrast to Agda, resolving a Prolog query need not terminate. 40 | Using coinduction, however, we can write an interpreter for Prolog 41 | that is \emph{total}. 42 | \item % 43 | Resolving a Prolog query results in a substitution that, when 44 | applied to the goal, produces a solution in the form of a term that 45 | can be derived from the given rules. We extend our interpreter to 46 | also produce a trace of the applied rules, which enables it to 47 | produce a proof term that shows the resulting substitution is valid. 48 | \item % 49 | We integrate this proof search algorithm with Agda's 50 | \emph{reflection} mechanism (Section~\ref{sec:reflection}). This 51 | enables us to \emph{quote} the type of a lemma we would like to 52 | prove, pass this term as the goal of our proof search algorithm, and 53 | finally, \emph{unquote} the resulting proof term, thereby proving 54 | the desired lemma. 55 | \end{itemize} 56 | Although Agda already has built-in proof search 57 | functionality~\citep{lindblad}, our approach has several key 58 | advantages over most existing approaches to proof automation: 59 | \begin{itemize} 60 | \item Our library is highly customizable. We may parametrize our 61 | tactics over the search depth, hint database, or search 62 | strategy. Each of these is itself a first-class Agda value, that may 63 | be inspected or transformed, depending on the user's needs. 64 | \item Although we limit ourself in the paper to a simple depth-first 65 | search, different proofs may require a different search strategy. 66 | Such changes are easily made in our library. To illustrate this 67 | point, we will develop a variation of our tactic which allows the 68 | user to limit the number of times certain rules may be applied 69 | (Section~\ref{sec:extensible}). 70 | \item Users need not learn a new programming language to modify 71 | existing tactics or develop tactics of their own. They can use a 72 | full-blown programming language to define their tactics, rather than 73 | restrict themselves to a domain-specific tactic language such as 74 | Ltac. 75 | \item Finally, users can use all the existing Agda technology for 76 | testing and debugging \emph{programs} when debugging the generation 77 | of \emph{proofs}. Debugging complex tactics in Coq requires a great 78 | deal of expertise -- we hope that implementing tactics as a library 79 | will make this process easier. 80 | \end{itemize} 81 | We will compare our library with the various alternative forms of 82 | proof automation in greater depth in Section~\ref{sec:discussion}, 83 | after we have presented our development. 84 | 85 | All the code described in this paper is freely available from 86 | GitHub.\footnote{ 87 | See \url{https://github.com/pepijnkokke/AutoInAgda}. 88 | } It is important to emphasize that all our code 89 | is written in the safe fragment of Agda: it does not depend on any 90 | postulates or foreign functions; all definitions pass Agda's 91 | termination checker; and all metavariables are resolved. 92 | 93 | %%% Local Variables: 94 | %%% mode: latex 95 | %%% TeX-master: t 96 | %%% TeX-command-default: "rake" 97 | %%% End: 98 | -------------------------------------------------------------------------------- /doc/main.bib: -------------------------------------------------------------------------------- 1 | 2 | @preamble{"\providecommand{\noopsort}[1]{}"} 3 | 4 | @inproceedings{malecha, 5 | title={Compositional Computational Reflection}, 6 | author={Malecha, Gregory and Chlipala, Adam and Braibant, Thomas}, 7 | booktitle={Proceedings of the 5th International Conference on Interactive Theorem Proving (ITP'14)}, 8 | year={2014} 9 | } 10 | 11 | 12 | @inproceedings{kipling, 13 | Acmid = {1863497}, 14 | Address = {New York, NY, USA}, 15 | Author = {McBride, Conor}, 16 | Booktitle = {Proceedings of the 6th ACM SIGPLAN Workshop on Generic Programming}, 17 | Doi = {10.1145/1863495.1863497}, 18 | Location = {Baltimore, Maryland, USA}, 19 | Numpages = {12}, 20 | Pages = {1--12}, 21 | Publisher = {ACM}, 22 | Series = {WGP '10}, 23 | Title = {Outrageous but Meaningful Coincidences: Dependent Type-safe Syntax and Evaluation}, 24 | Year = {2010}, 25 | } 26 | 27 | @inproceedings{nisse, 28 | Author = {Nils Anders Danielsson}, 29 | Booktitle = {Types for Proofs and Programs}, 30 | Date-Added = {2014-02-24 12:48:43 +0000}, 31 | Date-Modified = {2014-02-24 12:50:02 +0000}, 32 | Publisher = {Spring Verlag}, 33 | Series = {Lecture Notes in Computer Science}, 34 | Title = {A Formalisation of a Dependently Typed Language as an Inductive-Recursive Family}, 35 | Volume = {4502}, 36 | Year = {2006}} 37 | 38 | @phdthesis{james-phd, 39 | Author = {James Chapman}, 40 | Date-Added = {2014-02-24 12:48:18 +0000}, 41 | Date-Modified = {2014-02-24 12:48:40 +0000}, 42 | School = {University of Nottingham}, 43 | Title = {Type checking and normalisation}, 44 | Year = {2009}} 45 | 46 | @misc{agda-relnotes-228, 47 | Author = {{Agda development team}}, 48 | Date-Added = {2014-01-17 14:49:17 +0000}, 49 | Date-Modified = {2014-01-17 14:49:17 +0000}, 50 | Howpublished = {The Agda Wiki: \url{http://wiki.portal.chalmers.se/agda/agda.php?n=Main.Version-2-2-8} and \url{http://wiki.portal.chalmers.se/agda/agda.php?n=Main.Version-2-3-0}}, 51 | Note = {[Online; accessed 9-Feb-2013]}, 52 | Title = {Agda release notes documenting the reflection mechanism}, 53 | Year = 2013} 54 | 55 | @mastersthesis{vdWalt:Thesis:2012, 56 | Address = {Utrecht, The Netherlands}, 57 | Author = {{\noopsort{Walt}}{van der Walt}, Paul}, 58 | Date-Added = {2014-01-17 14:49:12 +0000}, 59 | Date-Modified = {2014-01-17 14:49:12 +0000}, 60 | Note = {Available online, \url{http://igitur-archive.library.uu.nl/student-theses/2012-1030-200720/UUindex.html}}, 61 | School = {Department of Computer Science, Utrecht University}, 62 | Title = {{Reflection in Agda}}, 63 | Year = 2012} 64 | 65 | @inproceedings{template-haskell, 66 | Author = {Tim Sheard and Simon {Peyton Jones}}, 67 | Booktitle = {{Proceedings of the 2002 ACM SIGPLAN Workshop on Haskell}}, 68 | Date-Added = {2014-01-17 14:47:58 +0000}, 69 | Date-Modified = {2014-01-17 14:47:58 +0000}, 70 | Doi = {10.1145/581690.581691}, 71 | Pages = {1--16}, 72 | Title = {Template meta-programming for {Haskell}}, 73 | Year = 2002, 74 | Bdsk-Url-1 = {http://doi.acm.org/10.1145/581690.581691}, 75 | Bdsk-Url-2 = {http://dx.doi.org/10.1145/581690.581691}} 76 | 77 | @inproceedings{metaml, 78 | Author = {Taha, Walid and Sheard, Tim}, 79 | Booktitle = {{Proceedings of the 1997 ACM SIGPLAN Symposium on Partial evaluation and semantics-based program manipulation}}, 80 | Date-Added = {2014-01-17 14:47:54 +0000}, 81 | Date-Modified = {2014-01-17 14:47:54 +0000}, 82 | Doi = {10.1145/258993.259019}, 83 | Location = {Amsterdam, The Netherlands}, 84 | Series = {PEPM '97}, 85 | Title = {Multi-stage programming with explicit annotations}, 86 | Year = 1997, 87 | Bdsk-Url-1 = {http://doi.acm.org/10.1145/258993.259019}, 88 | Bdsk-Url-2 = {http://dx.doi.org/10.1145/258993.259019}} 89 | 90 | @inproceedings{lisp-macros, 91 | Author = {Pitman, Kent M.}, 92 | Booktitle = {Proceedings of the 1980 {ACM} conference on {LISP} and {Functional Programming}}, 93 | Date-Added = {2014-01-17 14:47:36 +0000}, 94 | Date-Modified = {2014-01-17 14:47:36 +0000}, 95 | Organization = {ACM}, 96 | Pages = {179--187}, 97 | Title = {Special forms in {Lisp}}, 98 | Year = {1980}} 99 | 100 | @misc{brabaint, 101 | Author = {Thomas Braibant}, 102 | Date-Added = {2014-01-16 14:06:44 +0000}, 103 | Date-Modified = {2014-01-16 14:08:34 +0000}, 104 | Howpublished = {Available online \url{http://gallium.inria.fr/blog/your-first-coq-plugin/}}, 105 | Title = {Emancipate yourself from {Ltac}}, 106 | Year = {2012}} 107 | 108 | @incollection{van-der-walt, 109 | Author = {Paul van der Walt and Wouter Swierstra}, 110 | Booktitle = {Implementation and Application of Functional Languages}, 111 | Date-Added = {2014-01-16 14:06:06 +0000}, 112 | Date-Modified = {2014-01-16 14:06:24 +0000}, 113 | Doi = {10.1007/978-3-642-41582-1_10}, 114 | Editor = {Hinze, Ralf}, 115 | Language = {English}, 116 | Pages = {157-173}, 117 | Publisher = {Springer Berlin Heidelberg}, 118 | Series = {Lecture Notes in Computer Science}, 119 | Title = {Engineering Proof by Reflection in {Agda}}, 120 | Year = {2013}, 121 | Bdsk-Url-1 = {http://dx.doi.org/10.1007/978-3-642-41582-1_10}} 122 | 123 | @inproceedings{stutterheim, 124 | Author = {Stutterheim, Jurri\"en and Swierstra, Wouter and Swierstra, Doaitse}, 125 | Booktitle = {Proceedings First International Workshop on Trends in Functional Programming in Education, University of St. Andrews, Scotland, UK, 11th June 2012}, 126 | Date-Added = {2014-01-16 14:02:19 +0000}, 127 | Date-Modified = {2014-01-16 14:10:21 +0000}, 128 | Pages = {50--62}, 129 | Series = {Electronic Proceedings in Theoretical Computer Science}, 130 | Title = {Forty hours of declarative programming: Teaching {Prolog} at the {Junior College Utrecht}}, 131 | Volume = {106}, 132 | Year = {2013}} 133 | 134 | @misc{ulf-tphols, 135 | Author = {Ulf Norell}, 136 | Date-Added = {2014-01-16 13:59:34 +0000}, 137 | Date-Modified = {2014-01-16 13:59:55 +0000}, 138 | Howpublished = {Invited talk at TPHOLS}, 139 | Title = {Playing with {Agda}}, 140 | Year = {2009}} 141 | 142 | @incollection{agda-tutorial, 143 | title={Dependently typed programming in {Agda}}, 144 | author={Norell, Ulf}, 145 | booktitle={Advanced Functional Programming}, 146 | pages={230--266}, 147 | year={2009}, 148 | publisher={Springer} 149 | } 150 | 151 | @phdthesis{agda, 152 | Author = {Ulf Norell}, 153 | Date-Added = {2014-01-16 13:58:49 +0000}, 154 | Date-Modified = {2014-01-16 14:00:03 +0000}, 155 | School = {Department of Computer Science and Engineering, Chalmers University of Technology}, 156 | Title = {Towards a practical programming language based on dependent type theory}, 157 | Year = {2007}} 158 | 159 | @misc{coq, 160 | Author = {Coq development team}, 161 | Date-Added = {2014-01-16 13:58:02 +0000}, 162 | Date-Modified = {2014-01-16 13:58:37 +0000}, 163 | Howpublished = {Logical Project}, 164 | Title = {The {Coq} proof assistant reference manual}, 165 | Year = {2004}} 166 | 167 | @inproceedings{lindblad, 168 | Author = {Fredrik Lindblad and Marcin Benke}, 169 | Booktitle = {Proceedings of the 2004 International Conference on Types for Proofs and Programs}, 170 | Date-Added = {2014-01-16 13:57:00 +0000}, 171 | Date-Modified = {2014-01-16 13:58:01 +0000}, 172 | Pages = {154--169}, 173 | Publisher = {Springer-Verlag}, 174 | Title = {A tool for automated theorem proving in {Agda}}, 175 | Year = {2004}} 176 | 177 | @misc{ring, 178 | Author = {Wojciech Jedynak}, 179 | Date-Added = {2014-01-16 13:56:21 +0000}, 180 | Date-Modified = {2014-01-16 13:56:54 +0000}, 181 | Howpublished = {Code available from \url{https://github.com/wjzz/Agda-reflection-for-semiring-solver},}, 182 | Title = {Agda semiring solver}, 183 | Year = {2013}} 184 | 185 | @book{chlipala, 186 | Author = {Adam Chlipala}, 187 | Date-Added = {2014-01-16 13:55:57 +0000}, 188 | Date-Modified = {2014-01-16 14:09:06 +0000}, 189 | Publisher = {MIT Press}, 190 | Title = {Certified programming with dependent types}, 191 | Year = {2013}} 192 | 193 | @unpublished{allais, 194 | Author = {Guillaume Allais}, 195 | Date-Added = {2014-01-16 13:55:16 +0000}, 196 | Date-Modified = {2014-01-16 14:09:31 +0000}, 197 | Note = {MSc Intern report, University of Nottingham}, 198 | Title = {Proof automatization using reflection (implementations in {Agda})}, 199 | Year = {2010}} 200 | 201 | @article{unification, 202 | Author = {Conor McBride}, 203 | Doi = {10.1017/S0956796803004957}, 204 | Issue = {06}, 205 | Journal = {Journal of Functional Programming}, 206 | Month = {11}, 207 | Numpages = {15}, 208 | Pages = {1061--1075}, 209 | Title = {First-order unification by structural recursion}, 210 | Volume = {13}, 211 | Year = {2003}, 212 | Bdsk-Url-1 = {http://journals.cambridge.org/article_S0956796803004957}, 213 | Bdsk-Url-2 = {http://dx.doi.org/10.1017/S0956796803004957}} 214 | 215 | @article{compare, 216 | Acmid = {967496}, 217 | Address = {New York, NY, USA}, 218 | Author = {McBride, Conor and McKinna, James}, 219 | Doi = {10.1017/S0956796803004829}, 220 | Issn = {0956-7968}, 221 | Issue_Date = {January 2004}, 222 | Journal = {J. Funct. Program.}, 223 | Month = jan, 224 | Number = {1}, 225 | Numpages = {43}, 226 | Pages = {69--111}, 227 | Publisher = {Cambridge University Press}, 228 | Title = {The View from the Left}, 229 | Url = {http://dx.doi.org/10.1017/S0956796803004829}, 230 | Volume = {14}, 231 | Year = {2004}, 232 | Bdsk-Url-1 = {http://dx.doi.org/10.1017/S0956796803004829}} 233 | 234 | @inproceedings{instance-args, 235 | Author = {Devriese, Dominique and Piessens, Frank}, 236 | Booktitle = {Proceedings of the 16th ACM SIGPLAN International Conference on Functional Programming}, 237 | Doi = {10.1145/2034773.2034796}, 238 | Location = {Tokyo, Japan}, 239 | Pages = {143--155}, 240 | Publisher = {ACM}, 241 | Series = {ICFP '11}, 242 | Title = {On the Bright Side of Type Classes: Instance Arguments in {Agda}}, 243 | Year = {2011}, 244 | Bdsk-Url-1 = {http://dx.doi.org/10.1145/2034773.2034796}} 245 | 246 | @article{idris, 247 | Author = {Brady,Edwin}, 248 | Doi = {10.1017/S095679681300018X}, 249 | Issue = {05}, 250 | Journal = {Journal of Functional Programming}, 251 | Month = {9}, 252 | Numpages = {42}, 253 | Pages = {552--593}, 254 | Title = {Idris, a general-purpose dependently typed programming language: Design and implementation}, 255 | Volume = {23}, 256 | Year = {2013} 257 | } 258 | 259 | @incollection{coq-type-classes, 260 | Author = {Sozeau, Matthieu and Oury, Nicolas}, 261 | Booktitle = {Theorem Proving in Higher Order Logics}, 262 | Pages = {278--293}, 263 | Publisher = {Springer}, 264 | Title = {First-class type classes}, 265 | Year = {2008}} 266 | 267 | @book{haskell-report, 268 | Editor = {Peyton Jones, Simon}, 269 | Publisher = {Cambridge University Press}, 270 | Title = {Haskell 98 language and libraries: the revised report}, 271 | Year = {2003}} 272 | 273 | @incollection{brady-opt, 274 | Author = {Brady, Edwin and McBride, Conor and McKinna, James}, 275 | Booktitle = {Types for Proofs and Programs}, 276 | Pages = {115--129}, 277 | Publisher = {Springer}, 278 | Title = {Inductive families need not store their indices}, 279 | Year = {2004}} 280 | 281 | @inproceedings{devriese, 282 | author = {Devriese, Dominique and Piessens, Frank}, 283 | title = {Typed syntactic meta-programming}, 284 | booktitle = {Proceedings of the 2013 ACM SIGPLAN International Conference on Functional Programming (ICFP 2013)}, 285 | year = {2013}, 286 | month = {September}, 287 | publisher = {ACM}, 288 | doi = {10.1145/2500365.2500575} 289 | } 290 | 291 | @inproceedings{oury, 292 | author = {Oury, Nicolas and Swierstra, Wouter}, 293 | title = {The {Power} of {Pi}}, 294 | booktitle = {Proceedings of the 13th ACM SIGPLAN International Conference on Functional Programming}, 295 | series = {ICFP '08}, 296 | year = {2008}, 297 | location = {Victoria, BC, Canada}, 298 | pages = {39--50}, 299 | numpages = {12}, 300 | doi = {10.1145/1411204.1411213} 301 | } 302 | 303 | @article{swierstra-more, 304 | title={More dependent types for distributed arrays}, 305 | author={Swierstra, Wouter}, 306 | journal={Higher-order and symbolic computation}, 307 | volume={23}, 308 | number={4}, 309 | pages={489--506}, 310 | year={2010}, 311 | publisher={Springer US} 312 | } 313 | 314 | @book{hol, 315 | title={Introduction to HOL: a theorem proving environment for higher order logic}, 316 | author={Gordon, M.J.C and Melham, T.F.}, 317 | year={1993}, 318 | publisher={Cambridge University Press} 319 | } 320 | 321 | @inproceedings{mtac, 322 | author = {Ziliani, Beta and Dreyer, Derek and Krishnaswami, Neelakantan R. and Nanevski, Aleksandar and Vafeiadis, Viktor}, 323 | title = {Mtac: A Monad for Typed Tactic Programming in {Coq}}, 324 | booktitle = {Proceedings of the 18th ACM SIGPLAN International Conference on Functional Programming}, 325 | series = {ICFP '13}, 326 | year = {2013}, 327 | location = {Boston, Massachusetts, USA}, 328 | pages = {87--100}, 329 | numpages = {14}, 330 | doi = {10.1145/2500365.2500579} 331 | } 332 | 333 | @inproceedings{martin-lof, 334 | title={Constructive mathematics and computer programming}, 335 | author={Martin-L{\"o}f, Per}, 336 | booktitle={Proceedings of a discussion meeting of the Royal Society 337 | of London on Mathematical logic and programming 338 | languages}, 339 | pages={167--184}, 340 | year={1985}, 341 | organization={Prentice-Hall, Inc.} 342 | } 343 | 344 | @article{vannoort, 345 | author = {Swierstra,Wouter and van Noort,Thomas}, 346 | title = {A library for polymorphic dynamic typing}, 347 | journal = {Journal of Functional Programming}, 348 | volume = {23}, 349 | issue = {03}, 350 | month = {5}, 351 | year = {2013}, 352 | pages = {229--248}, 353 | numpages = {20}, 354 | doi = {10.1017/S0956796813000063} 355 | } -------------------------------------------------------------------------------- /doc/main.fmt: -------------------------------------------------------------------------------- 1 | %format hole = "\{\ \}0" 2 | %format quoteGoal = "\textsf{\textbf{quoteGoal}}" 3 | %format quote = "\textsf{\textbf{quote}}" 4 | %format quoteTerm = "\textsf{\textbf{quoteTerm}}" 5 | %format unquote = "\textsf{\textbf{unquote}}" 6 | %format tactic = "\textsf{\textbf{tactic}}" 7 | 8 | -------------------------------------------------------------------------------- /doc/main.lagda: -------------------------------------------------------------------------------- 1 | \documentclass[preprint]{llncs} 2 | 3 | %include agda.fmt 4 | %include main.fmt 5 | \include{preamble} 6 | 7 | \begin{document} 8 | 9 | \title{Auto in Agda} 10 | \subtitle{Programming proof search using reflection} 11 | 12 | \author{Pepijn Kokke \and Wouter Swierstra} 13 | 14 | \institute{Universiteit Utrecht\\ \email{pepijn.kokke@@gmail \quad w.s.swierstra@@uu.nl}} 15 | 16 | \maketitle 17 | 18 | \begin{abstract} 19 | 20 | As proofs in type theory become increasingly complex, there is a 21 | growing need to provide better proof automation. This paper shows 22 | how to implement a Prolog-style resolution procedure in the 23 | dependently typed programming language Agda. Connecting this 24 | resolution procedure to Agda's reflection mechanism provides a 25 | first-class proof search tactic for first-order Agda 26 | terms. As a result, writing proof automation tactics need not be 27 | different from writing any other program. 28 | 29 | \end{abstract} 30 | 31 | %include intro.lagda 32 | %include motivation.lagda 33 | %include prolog.lagda 34 | %include reflection.lagda 35 | %include extensible.lagda 36 | %include discussion.lagda 37 | 38 | \paragraph{Acknowledgements} 39 | We would like to thank the Software Technology Reading Club at the 40 | Universiteit Utrecht, and all our anonymous reviewers for their helpful 41 | feedback -- we hope we have done their feedback justice. 42 | 43 | \bibliographystyle{plainnat} 44 | \bibliography{main} 45 | 46 | \end{document} 47 | 48 | %%% Local Variables: 49 | %%% mode: latex 50 | %%% TeX-master: t 51 | %%% TeX-command-default: "rake" 52 | %%% End: 53 | -------------------------------------------------------------------------------- /doc/motivation.lagda: -------------------------------------------------------------------------------- 1 | \section{Motivation} 2 | \label{sec:motivation} 3 | 4 | Before describing the \emph{implementation} of our library, we will 5 | provide a brief introduction to Agda's reflection mechanism and 6 | illustrate how the proof automation described in this paper may be 7 | used. 8 | 9 | \subsection*{Reflection in Agda} 10 | 11 | Agda has a \emph{reflection} mechanism\footnote{Note that Agda's 12 | reflection mechanism should not be confused with `proof by 13 | reflection' -- the technique of writing a verified decision 14 | procedure for some class of problems.} for compile time 15 | metaprogramming in the style of Lisp~\citep{lisp-macros}, 16 | MetaML~\citep{metaml}, and Template 17 | Haskell~\citep{template-haskell}. This reflection mechanism makes it 18 | possible to convert a program fragment into its corresponding abstract 19 | syntax tree and vice versa. We will introduce Agda's reflection 20 | mechanism here with several short examples, based on the explanation 21 | in previous work~\citep{van-der-walt}. A more complete overview can be 22 | found in the Agda release notes~\citep{agda-relnotes-228} and Van der 23 | Walt's thesis~\citeyearpar{vdWalt:Thesis:2012}. 24 | 25 | The type |Term : Set| is the central type provided by the reflection mechanism. 26 | It defines an abstract syntax tree for Agda terms. There are several 27 | language constructs for quoting and unquoting program fragments. The simplest 28 | example of the reflection mechanism is the quotation of a single 29 | term. In the definition of |idTerm| below, we quote the identity 30 | function on Boolean values. 31 | \begin{code} 32 | idTerm : Term 33 | idTerm = quoteTerm (λ (x : Bool) → x) 34 | \end{code} 35 | When evaluated, the |idTerm| yields the following value: 36 | \begin{code} 37 | lam visible (var 0 []) 38 | \end{code} 39 | On the outermost level, the |lam| constructor produces a lambda 40 | abstraction. It has a single argument that is passed explicitly (as 41 | opposed to Agda's implicit arguments). The body of the lambda consists 42 | of the variable identified by the De Bruijn index 0, applied to an 43 | empty list of arguments. 44 | 45 | The |quote| language construct allows users to access the internal 46 | representation of an \emph{identifier}, a value of a built-in type 47 | |Name|. Users can subsequently request the type or definition of such 48 | names. 49 | 50 | Dual to quotation, the |unquote| mechanism allows users to splice in a 51 | |Term|, replacing it with its concrete syntax. For example, we could 52 | give a convoluted definition of the |K| combinator as follows: 53 | \begin{code} 54 | const : ∀ {A B} → A → B → A 55 | const = unquote (lam visible (lam visible (var 1 []))) 56 | \end{code} 57 | The language construct |unquote| is followed by a value of type 58 | |Term|. In this example, we manually construct a |Term| representing 59 | the |K| combinator and splice it in the definition of |const|. The 60 | |unquote| construct then type-checks the given term, and turns it into 61 | the definition |λ x → λ y → x|. 62 | 63 | The final piece of the reflection mechanism that we will use is the 64 | |quoteGoal| construct. The usage of |quoteGoal| is best illustrated 65 | with an example: 66 | \begin{code} 67 | goalInHole : ℕ 68 | goalInHole = quoteGoal g in hole 69 | \end{code} 70 | In this example, the construct |quoteGoal g| binds the |Term| 71 | representing the \emph{type} of the current goal, |ℕ|, to the variable 72 | |g|. When completing this definition by filling in the hole labeled 73 | |0|, we may now refer to the variable |g|. This variable is bound to 74 | |def ℕ []|, the |Term| representing the type |ℕ|. 75 | 76 | \subsection*{Using proof automation} 77 | 78 | To illustrate the usage of our proof automation, we begin by defining a 79 | predicate |Even| on natural numbers as follows: 80 | 81 | \begin{code} 82 | data Even : ℕ → Set where 83 | isEven0 : Even 0 84 | isEven+2 : ∀ {n} → Even n → Even (suc (suc n)) 85 | \end{code} 86 | % 87 | Next we may want to prove properties of this definition: 88 | % 89 | \begin{code} 90 | even+ : Even n → Even m → Even (n + m) 91 | even+ isEven0 e2 = e2 92 | even+ ( isEven+2 e1) e2 = isEven+2 (even+ e1 e2) 93 | \end{code} 94 | % 95 | Note that we omit universally quantified implicit arguments from the 96 | typeset version of this paper, in accordance with convention used by 97 | Haskell~\citep{haskell-report} and Idris~\citep{idris}. 98 | 99 | As shown by Van der Walt and Swierstra~\citeyearpar{van-der-walt}, it is easy 100 | to decide the |Even| property for closed terms using proof by 101 | reflection. The interesting terms, however, are seldom closed. For 102 | instance, if we would like to use the |even+| lemma in the proof 103 | below, we need to call it explicitly. 104 | 105 | \begin{code} 106 | trivial : Even n → Even (n + 2) 107 | trivial e = even+ e (isEven+2 isEven0) 108 | \end{code} 109 | Manually constructing explicit proof objects 110 | in this fashion is not easy. The proof is brittle. We cannot easily 111 | reuse it to prove similar statements such as |Even (n + 4)|. If we 112 | need to reformulate our statement slightly, proving |Even (2 + n)| 113 | instead, we need to rewrite our proof. Proof automation can make 114 | propositions more robust against such changes. 115 | 116 | Coq's proof search tactics, such as |auto|, can be customized with a 117 | \emph{hint database}, a collection of related lemmas. In our 118 | example, |auto| would be able to prove the |trivial| lemma, provided 119 | the hint database contains at least the constructors of the |Even| 120 | data type and the |even+| lemma. 121 | In 122 | contrast to the construction of explicit proof terms, changes to the 123 | theorem statement need not break the proof. This paper shows how to 124 | implement a similar tactic as an ordinary function in Agda. 125 | 126 | Before we can use our |auto| function, we need to construct a hint 127 | database: 128 | \begin{code} 129 | hints : HintDB 130 | hints = ε << quote isEven0 << quote isEven+2 << quote even+ 131 | \end{code} 132 | To construct such a database, we use |quote| to obtain the names of any 133 | terms that we wish to include in it and pass them to the right-hand 134 | side of the |_<<_| function, which will insert them into a hint 135 | database to the left. Note that |ε| represents the empty hint 136 | database. 137 | We will describe the implementation of |_<<_| in more detail in 138 | Section~\ref{sec:hintdbs}. 139 | For now it should suffice to say that, in the case of |even+|, after 140 | the |quote| construct obtains an Agda |Name|, |_<<_| uses the built-in 141 | function |type| to look up the type associated with |even+|, and 142 | generates a derivation rule which states that given two proofs of 143 | |Even n| and |Even m|, applying the rule |even+| will result in a 144 | proof of |Even (n + m)|. 145 | 146 | Note, however, that unlike Coq, the hint data base is a 147 | \emph{first-class} value that can be manipulated, inspected, or passed 148 | as an argument to a function. 149 | 150 | We now give an alternative proof of the |trivial| lemma using the 151 | |auto| tactic and the hint database defined above: 152 | \begin{code} 153 | trivial : Even n → Even (n + 2) 154 | trivial = quoteGoal g in unquote (auto 5 hints g) 155 | \end{code} 156 | Or, using the newly added Agda tactic syntax\footnote{ 157 | Syntax for Agda tactics was added in Agda 2.4.2. 158 | }: 159 | \begin{code} 160 | trivial : Even n → Even (n + 2) 161 | trivial = tactic (auto 5 hints) 162 | \end{code} 163 | The notation |tactic f| is simply syntactic sugar for |quoteGoal g in 164 | unquote (f g)|, for some function |f|. 165 | 166 | The central ingredient is a \emph{function} |auto| with the following 167 | type: 168 | \begin{code} 169 | auto : (depth : ℕ) → HintDB → Term → Term 170 | \end{code} 171 | Given a maximum depth, hint database, and goal, it searches for a 172 | proof |Term| that witnesses our goal. If this term can be found, it is 173 | spliced back into our program using the |unquote| statement. 174 | 175 | Of course, such invocations of the |auto| function may fail. What 176 | happens if no proof exists? For example, trying to prove |Even n → 177 | Even (n + 3)| in this style gives the following error: 178 | \begin{verbatim} 179 | Exception searchSpaceExhausted !=< 180 | Even .n -> Even (.n + 3) of type Set 181 | \end{verbatim} 182 | When no proof can be found, the |auto| function generates a dummy 183 | term with a type that explains the reason the search has failed. In 184 | this example, the search space has been exhausted. Unquoting this 185 | term, then gives the type error message above. It is up to the 186 | programmer to fix this, either by providing a manual proof or 187 | diagnosing why no proof could be found. 188 | 189 | \paragraph{Overview} 190 | The remainder of this paper describes how the |auto| function is 191 | implemented. Before delving into the details of its implementation, 192 | however, we will give a high-level overview of the steps involved: 193 | \begin{enumerate} 194 | \item The |tactic| keyword converts the goal type to an abstract 195 | syntax tree, i.e., a value of type |Term|. In what follows we will 196 | use |AgTerm| to denote such terms, to avoid confusion with the other 197 | term data type that we use. 198 | \item Next, we check the goal term. If it has a functional type, we add 199 | the arguments of this function to our hint database, implicitly introducing 200 | additional lambdas to the proof term we intend to construct. At this point we check that 201 | the remaining type and all its original arguments are 202 | are first-order. If this check fails, we produce an error 203 | message, not unlike the |searchSpaceExhausted| term we saw 204 | above. We require terms to be first-order to ensure that the 205 | unification algorithm, used in later steps for proof search, is 206 | decidable. If the goal term is first-order, we convert it to our own 207 | term data type for proof search, |PsTerm|. 208 | \item The key proof search algorithm, presented in the next section, 209 | then tries to apply the hints from the hint database to prove the 210 | goal. This process coinductively generates a (potentially infinite) 211 | search tree. A simple bounded depth-first search through this tree 212 | tries to find a series of hints that can be used to prove the goal. 213 | \item If such a proof is found, this is converted back to an 214 | |AgTerm|; otherwise, we produce an erroneous term describing that 215 | the search space has been exhausted. Finally, the |unquote| keyword 216 | type checks the generated |AgTerm| and splices it back into our 217 | development. 218 | \end{enumerate} 219 | 220 | % But the biggest problem is that the paper doesn't clearly separate 221 | % what in the code is a good idea, what is an engineering trick, and 222 | % what is wart required to satisfy Agda. A discussion at that level 223 | % would be very informative and useful. 224 | % 225 | % Tricks + Warts 226 | % * Finite types, shifting indices, Generation of fresh variables 227 | % * 'Plumbing' reflection-conversion 228 | % * Proof obligations? regarding syntax 229 | % * constructing incomplete proofs 230 | 231 | \noindent The rest of this paper will explain these steps in 232 | greater detail. 233 | 234 | 235 | 236 | %%% Local Variables: 237 | %%% mode: latex 238 | %%% TeX-master: t 239 | %%% TeX-command-default: "rake" 240 | %%% End: 241 | -------------------------------------------------------------------------------- /doc/preamble.tex: -------------------------------------------------------------------------------- 1 | % Packages 2 | \usepackage{amsmath}% 3 | \usepackage{enumitem}% 4 | \usepackage{tikz}% 5 | \usepackage{tikz-qtree}% 6 | \usepackage{subfigure}% 7 | \usetikzlibrary{arrows,positioning}% 8 | \usepackage{listings}% 9 | \usepackage{url}% 10 | \usepackage{wasysym}% 11 | \usepackage{natbib}% 12 | 13 | % Unicode support 14 | \usepackage{textgreek} 15 | \usepackage{ucs} 16 | \usepackage[utf8x]{inputenc} 17 | 18 | \DeclareUnicodeCharacter{948}{\ensuremath{\delta}} 19 | \DeclareUnicodeCharacter{955}{\ensuremath{\lambda}} 20 | 21 | % Coloured comments 22 | \usepackage{color} 23 | \usepackage{ifthen} 24 | \newboolean{showNotes} 25 | \newboolean{marginNotes} 26 | \setboolean{showNotes}{true} 27 | \setboolean{marginNotes}{false} 28 | \newcommand{\marginNote}[1]{ 29 | \ifthenelse 30 | {\boolean{marginNotes}} 31 | {\marginpar{#1}} 32 | {#1}} 33 | 34 | \newcommand{\todo}[1]{ 35 | \ifthenelse 36 | {\boolean{showNotes}} 37 | {\marginNote{\textcolor{red}{\textbf{Todo:~}#1}}} 38 | {}} 39 | 40 | \newcommand{\wouter}[1]{ 41 | \ifthenelse 42 | {\boolean{showNotes}} 43 | {\marginNote{\textcolor{blue}{\textbf{Wouter:~}#1}}} 44 | {}} 45 | 46 | \newcommand{\pepijn}[1]{ 47 | \ifthenelse 48 | {\boolean{showNotes}} 49 | {\marginNote{\textcolor{blue}{\textbf{Pepijn:~}#1}}} 50 | {}} 51 | 52 | 53 | \newcommand{\review}[1]{ 54 | \ifthenelse 55 | {\boolean{showNotes}} 56 | {\marginNote{\textcolor{red}{\textbf{Review:~}#1}}} 57 | {}} 58 | -------------------------------------------------------------------------------- /doc/prolog.lagda: -------------------------------------------------------------------------------- 1 | \section{Proof search in Agda} 2 | \label{sec:prolog} 3 | 4 | The following section describes our implementation of proof search 5 | à la Prolog in Agda. This implementation abstracts over two data types 6 | for names---one for inference rules and one for term constructors. 7 | These data types will be referred to as |RuleName| and |TermName|, and 8 | will be instantiated with concrete types (with the same names) in 9 | section~\ref{sec:reflection}. 10 | 11 | 12 | \subsection*{Terms and unification} 13 | \label{subsec:terms} 14 | 15 | The heart of our proof search implementation is the structurally 16 | recursive unification algorithm described by~\citet{unification}. Here 17 | the type of terms is indexed by the number of variables a given term 18 | may contain. Doing so enables the formulation of the unification 19 | algorithm by structural induction on the number of free variables. 20 | For this to work, we will use the following definition of 21 | terms 22 | \begin{code} 23 | data PsTerm (n : ℕ) : Set where 24 | var : Fin n → PsTerm n 25 | con : TermName → List (PsTerm n) → PsTerm n 26 | \end{code} 27 | We will use the name |PsTerm| to stand for \emph{proof search term} to 28 | differentiate them from the terms from Agda's \emph{reflection} 29 | mechanism, |AgTerm|. In addition to variables, represented by the 30 | finite type |Fin n|, we will allow first-order constants encoded as a 31 | name with a list of arguments. 32 | 33 | For instance, if we choose to instantiate |TermName| with the following 34 | |Arith| data type, we can encode numbers and simple arithmetic 35 | expressions: 36 | \begin{code} 37 | data Arith : Set where 38 | Suc : Arith 39 | Zero : Arith 40 | Add : Arith 41 | \end{code} 42 | The closed term corresponding to the number one could be written as follows: 43 | \begin{code} 44 | One : PsTerm 0 45 | One = con Suc (con Zero [] ∷ []) 46 | \end{code} 47 | Similarly, we can use the |var| constructor to represent open terms, 48 | such as |x + 1|. We use the prefix operator |#| to convert from 49 | natural numbers to finite types: 50 | \begin{code} 51 | AddOne : PsTerm 1 52 | AddOne = con Add (var (# 0) ∷ One ∷ []) 53 | \end{code} 54 | Note that this representation of terms is untyped. There is no check 55 | that enforces addition is provided precisely two arguments. Although 56 | we could add further type information to this effect, this introduces 57 | additional overhead without adding safety to the proof automation 58 | presented in this paper. For the sake of simplicity, we have therefore 59 | chosen to work with this untyped definition. 60 | 61 | We shall refrain from further discussion of the unification algorithm itself. 62 | Instead, we restrict ourselves to presenting the interface that we will use: 63 | \begin{code} 64 | unify : (t₁ t₂ : PsTerm m) → Maybe (∃[ n ] Subst m n) 65 | \end{code} 66 | The |unify| function takes two terms |t₁| and |t₂| and tries to 67 | compute a substitution---the most general unifier. Substitutions are 68 | indexed by two natural numbers |m| and |n|. A substitution of type 69 | |Subst m n| can be applied to a |PsTerm m| to produce a value of type 70 | |PsTerm n|. 71 | As unification may fail, the result is wrapped in the |Maybe| type. In 72 | addition, since the number of variables in the terms resulting from 73 | the unifying substitution is not known \emph{a priori}, this 74 | number is existentially quantified over. 75 | For the remainder of the paper, we will write |∃[ x ] B| to mean a 76 | type |B| with occurrences of an existentially quantified variable |x|, 77 | or |∃ (λ x → B)| in full. 78 | 79 | 80 | \subsection*{Inference rules} 81 | \label{subsec:rules} 82 | 83 | The hints in the hint database will form \emph{inference rules} that 84 | we may use to prove a goal term. We represent such rules as records 85 | containing a rule name, a list of terms for its premises, and a term 86 | for its conclusion: 87 | \begin{code} 88 | record Rule (n : ℕ) : Set where 89 | field 90 | name : RuleName 91 | premises : List (PsTerm n) 92 | conclusion : PsTerm n 93 | 94 | arity : ℕ 95 | arity = length premises 96 | \end{code} 97 | Once again the data-type is quantified over the number of variables 98 | used in the rule. Note that the number of variables in the 99 | premises and the conclusion is the same. 100 | 101 | Using our newly defined |Rule| type we can give a simple definition of 102 | addition. In Prolog, this would be written as follows. 103 | \begin{verbatim} 104 | add(0, X, X). 105 | add(suc(X), Y, suc(Z)) :- add(X, Y, Z). 106 | \end{verbatim} 107 | Unfortunately, the named equivalents in our Agda implementation given 108 | in Figure~\ref{fig:rules} are a bit more verbose. Note that we have, 109 | for the sake of this example, instantiated the |RuleName| and 110 | |TermName| to |String| and |Arith| respectively. 111 | \begin{figure}[t] 112 | \centering 113 | \normalsize 114 | \begin{code} 115 | AddBase : Rule 1 116 | AddBase = record { 117 | name = "AddBase" 118 | conclusion = con Add ( con Zero [] 119 | ∷ var (# 0) 120 | ∷ var (# 0) 121 | ∷ []) 122 | premises = [] 123 | } 124 | 125 | AddStep : Rule 3 126 | AddStep = record { 127 | name = "AddStep" 128 | conclusion = con Add ( con Suc (var (# 0) ∷ []) 129 | ∷ var (# 1) 130 | ∷ con Suc (var (# 2) ∷ []) 131 | ∷ []) 132 | premises = con Add ( var (# 0) 133 | ∷ var (# 1) 134 | ∷ var (# 2) 135 | ∷ []) 136 | ∷ [] 137 | } 138 | \end{code} 139 | \caption{Agda representation of example rules} 140 | \label{fig:rules} 141 | \end{figure} 142 | 143 | A \emph{hint database} is nothing more than a list of rules. As the 144 | individual rules may have different numbers of variables, we 145 | existentially quantify these: 146 | \begin{code} 147 | HintDB : Set 148 | HintDB = List (∃[ n ] Rule n) 149 | \end{code} 150 | 151 | \subsection*{Generalised injection and raising} 152 | \label{subsec:injectandraise} 153 | 154 | Before we can implement some form of proof search, we need to define a pair of 155 | auxiliary functions. During proof resolution, we will work 156 | with terms and rules containing a different number of variables. We 157 | will use the following pair of functions, |inject| and |raise|, to 158 | weaken bound variables, that is, map values of type |Fin n| to some 159 | larger finite type. 160 | \begin{code} 161 | inject : ∀ {m} n → Fin m → Fin (m + n) 162 | inject n zero = zero 163 | inject n (suc i) = suc (inject n i) 164 | 165 | raise : ∀ m {n} → Fin n → Fin (m + n) 166 | raise zero i = i 167 | raise (suc m) i = suc (raise m i) 168 | \end{code} 169 | On the surface, the |inject| function appears to be the identity. When you 170 | make all the implicit arguments explicit, however, you will see that 171 | it sends the |zero| constructor in |Fin m| to the |zero| constructor 172 | of type |Fin (m + n)|. Hence, the |inject| function maps |Fin m| into the 173 | \emph{first} |m| elements of the type |Fin (m + n)|. Dually, the 174 | |raise| function maps |Fin n| into the \emph{last} |n| elements of the 175 | type |Fin (m + n)| by repeatedly applying the |suc| constructor. 176 | 177 | We can use |inject| and |raise| to define similar functions 178 | that work on our |Rule| and |PsTerm| data types, by mapping them over 179 | all the variables that they contain. 180 | 181 | 182 | \subsection*{Constructing the search tree} 183 | \label{subsec:searchtrees} 184 | 185 | Our proof search procedure is consists of two steps. First, we 186 | coinductively construct a (potentially infinite) search space; next, 187 | we will perform a bounded depth-first traversal of this space to find 188 | a proof of our goal. 189 | 190 | We will represent the search space as a (potentially) infinitely deep, but 191 | finitely branching rose tree. 192 | \begin{code} 193 | data SearchTree (A : Set) : Set where 194 | leaf : A → SearchTree A 195 | node : List (∞ (SearchTree A)) → SearchTree A 196 | \end{code} 197 | We will instantiate the type parameter |A| with a 198 | type representing proof terms. These terms consist of applications of 199 | rules, with a sub-proof for every premise. 200 | \begin{code} 201 | data Proof : Set where 202 | con : (name : RuleName) (args : List Proof) → Proof 203 | \end{code} 204 | Unfortunately, during the proof search we will have to work with 205 | \emph{partially complete} proof terms. 206 | 207 | Such partial completed proofs are represented by the |PartialProof| 208 | type. In contrast to the |Proof| data type, the |PartialProof| type 209 | may contain variables, hence the type takes an additional number as 210 | its argument: 211 | \begin{code} 212 | PartialProof : ℕ → Set 213 | PartialProof m = ∃[ k ] Vec (PsTerm m) k × (Vec Proof k → Proof) 214 | \end{code} 215 | A value of type |PartialProof m| records three separate pieces of 216 | information: 217 | \begin{itemize} 218 | \item a number |k|, representing the number of open subgoals; 219 | \item a vector of length |k|, recording the subgoals that are still 220 | open; 221 | \item a function that, given a vector of |k| proofs for each of the 222 | subgoals, will produce a complete proof of the original goal. 223 | \end{itemize} 224 | Next, we define the following function to help construct partial 225 | proof terms: 226 | \begin{code} 227 | apply : (r : Rule n) → Vec Proof (arity r + k) → Vec Proof (suc k) 228 | apply r xs = new ∷ rest 229 | where 230 | new = con (name r) (toList (take (arity r) xs)) 231 | rest = drop (arity r) xs 232 | \end{code} 233 | Given a |Rule| and a list of proofs of subgoals, this |apply| function 234 | takes the required sub-proofs from the vector, and creates a new proof 235 | by applying the argument rule to these sub-proofs. The result then consists of 236 | this new proof, together with any unused sub-proofs. This is essentially 237 | the `unflattening' of a rose tree. 238 | 239 | We can now finally return to our proof search algorithm. The 240 | |solveAcc| function forms the heart of the search procedure. Given a 241 | hint database and the current partially complete proof, it produces a 242 | |SearchTree| containing completed proofs. 243 | \begin{code} 244 | solveAcc : HintDB -> PartialProof (δ + m) → SearchTree Proof 245 | solveAcc rules (0 , [] , p) = leaf (p []) 246 | solveAcc rules (suc k , g ∷ gs , p) = node (map step rules) 247 | \end{code} 248 | If there are no remaining subgoals, i.e., the list in the second 249 | component of the |PartialProof| is empty, the search is finished. We 250 | construct a proof |p []|, and wrap this in the |leaf| constructor of 251 | the |SearchTree|. If we still have open subgoals, we have more work to 252 | do. In that case, we will try to apply every rule in our hint database 253 | to resolve this open goal---our rose tree has as many branches as 254 | there are hints in the hint database. The real work is done by the 255 | |step| function, locally defined in a where clause, that given the 256 | rule to apply, computes the remainder of the |SearchTree|. 257 | 258 | Before giving the definition of the |step| function, we will try to 259 | provide some intuition. Given a rule, the |step| function will try to 260 | unify its conclusion with the current subgoal |g|. When this succeeds, 261 | the premises of the rule are added to the list of open subgoals. When 262 | this fails, we return a |node| with no children, indicating that 263 | applying this rule can never prove our current goal. 264 | 265 | Carefully dealing with variables, however, introduces some 266 | complication, as the code for the |step| function illustrates: 267 | \begin{code} 268 | step : ∃[ δ ] (Rule δ) → ∞ (SearchTree Proof) 269 | step (δ , r) 270 | with unify (inject δ g) (raise m (conclusion r)) 271 | ... | nothing = ♯ node [] 272 | ... | just (n , mgu) = ♯ solveAcc prf 273 | where 274 | prf : PartialProof n 275 | prf = arity r + k , gs′ , (p ∘ apply r) 276 | where 277 | gs′ : Vec (Goal n) (arity r + k) 278 | gs′ = map (sub mgu) (raise m (fromList (premises r)) ++ inject δ gs) 279 | \end{code} 280 | Note that we use the function |sub| to apply a substitution to a term. This 281 | function is defined by McBride~\citeyearpar{unification}. 282 | 283 | The rule given to the |step| function may have a number of free 284 | variables of its own. As a result, all goals have to be injected into 285 | a larger domain which includes all current variables \emph{and} the 286 | new rule's variables. The rule's premises and conclusion are then also 287 | raised into this larger domain, to guarantee freshness of the rule 288 | variables. 289 | 290 | The definition of the |step| function attempts to |unify| the current 291 | subgoal |g| and conclusion of the rule |r|. If this fails, we can 292 | return |node []| immediately. If this succeeds, however, we build up a 293 | new partial proof, |prf|. This new partial proof, once again, consists 294 | of three parts: 295 | \begin{itemize} 296 | \item the number of open subgoals is incremented by |arity r|, i.e., the 297 | number of premises of the rule |r|. 298 | \item the vector of open subgoals |gs| is extended with the premises 299 | of |r|, after weakening the variables of appropriately. 300 | \item the function producing the final |Proof| object will, given the 301 | proofs of the premises of |r|, call |apply r| to create the desired 302 | |con| node in the final proof object. 303 | \end{itemize} 304 | 305 | The only remaining step, is to kick-off our proof search algorithm 306 | with a partial proof, consisting of a single goal. 307 | \begin{code} 308 | solve : (goal : PsTerm m) → HintDB → SearchTree Proof 309 | solve g rules = solveAcc (1 , g ∷ [] , head) 310 | \end{code} 311 | 312 | \subsection*{Searching for proofs} 313 | 314 | After all this construction, we are left with a simple tree structure, 315 | which we can traverse in search of solutions. For instance, we can 316 | define a bounded depth-first traversal. 317 | \begin{code} 318 | dfs : (depth : ℕ) → SearchTree A → List A 319 | dfs zero _ = [] 320 | dfs ( suc k) ( leaf x) = return x 321 | dfs ( suc k) ( node xs) = concatMap (λ x → dfs k (♭ x)) xs 322 | \end{code} 323 | It is fairly straightforward to define other traversal strategies, 324 | such as a breadth-first search. Similarly, we could define a function 325 | which traverses the search tree aided by some heuristic. We will 326 | explore further variations on search strategies in 327 | Section~\ref{sec:extensible}. 328 | 329 | %%% Local Variables: 330 | %%% mode: latex 331 | %%% TeX-master: t 332 | %%% TeX-command-default: "rake" 333 | %%% End: 334 | -------------------------------------------------------------------------------- /doc/reflection.lagda: -------------------------------------------------------------------------------- 1 | \section{Adding reflection} 2 | \label{sec:reflection} 3 | 4 | To complete the definition of our |auto| function, we still need to 5 | convert between Agda's built-in |AgTerm| data type and the data type 6 | required by our unification and resolution algorithms, 7 | |PsTerm|. Similarly, we will need to transform the |Proof| produced by 8 | our |solve| function to an |AgTerm| that can be unquoted. These 9 | are essential pieces of plumbing, necessary to provide the desired 10 | proof automation. While not conceptually difficult, this does expose 11 | some of the limitations and design choices of the |auto| function. If 12 | you are unfamiliar with the precise workings of the Agda reflection 13 | mechanism, you may want to skim this section. 14 | 15 | The first thing we will need are 16 | concrete definitions for the |TermName| and |RuleName| data types, 17 | which were parameters to the development presented in the previous 18 | section. 19 | It would be desirable to identify both types with Agda's |Name| type, 20 | but unfortunately Agda does not assign a name to the function 21 | space type operator, |_→_|; nor does Agda assign names to locally bound variables. 22 | To address this, we define two new data types |TermName| and |RuleName|. 23 | 24 | \noindent First, we define the |TermName| data type. 25 | \begin{code} 26 | data TermName : Set where 27 | name : Name → TermName 28 | pvar : ℕ → TermName 29 | impl : TermName 30 | \end{code} 31 | The |TermName| data type has three constructors. The |name| 32 | constructor embeds Agda's built-in |Name| in the |TermName| type. 33 | The |pvar| constructor describes locally bound variables, represented by 34 | their De Bruijn index. Note that the |pvar| constructor has nothing to 35 | do with |PsTerm|'s |var| constructor: it is not used to construct 36 | a Prolog variable, but rather to be able to refer to a local variable 37 | as a Prolog constant. 38 | Finally, |impl| explicitly represents the Agda function space. 39 | 40 | We define the |RuleName| type in a similar fashion. 41 | \begin{code} 42 | data RuleName : Set where 43 | name : Name → RuleName 44 | rvar : ℕ → RuleName 45 | \end{code} 46 | The |rvar| constructor is used to refer to Agda variables as 47 | rules. Its argument |i| corresponds to the variable's De Bruijn index 48 | -- the value of |i| can be used directly as an argument to the |var| 49 | constructor of Agda's |Term| data type. 50 | 51 | As we have seen in Section~\ref{sec:motivation}, the |auto| function 52 | may fail to find the desired proof. Furthermore, the conversion from 53 | |AgTerm| to |PsTerm| may also fail for various reasons. To 54 | handle such errors, we will work in the |Error| monad defined below: 55 | \begin{code} 56 | Error : (A : Set a) → Set a 57 | Error A = Message ⊎ A 58 | \end{code} 59 | Upon failure, the |auto| function will produce an error message. The 60 | corresponding |Message| type simply enumerates the possible sources of 61 | failure: 62 | \begin{code} 63 | data Message : Set where 64 | searchSpaceExhausted : Message 65 | unsupportedSyntax : Message 66 | \end{code} 67 | The meaning of each of these error messages will be explained as we 68 | encounter them in our implementation below. 69 | 70 | Finally, we will need one more auxiliary function to manipulate bound 71 | variables. The |match| function takes two bound variables of types 72 | |Fin m| and |Fin n| and computes the corresponding variables in |Fin 73 | (m ⊔ n)| -- where |m ⊔ n| denotes the maximum of |m| and |n|: 74 | \begin{code} 75 | match : Fin m → Fin n → Fin (m ⊔ n) × Fin (m ⊔ n) 76 | \end{code} 77 | The implementation is reasonably straightforward. We compare the 78 | numbers |n| and |m|, and use the |inject| function to weaken the 79 | appropriate bound variable. It is straightforward to use this |match| 80 | function to define similar operations on two terms or a term and a 81 | list of terms. 82 | 83 | \subsection*{Constructing terms} 84 | 85 | We now turn our attention to the conversion of an |AgTerm| to a 86 | |PsTerm|. There are two problems that we must address. 87 | 88 | First of all, the |AgTerm| type represents all (possibly higher-order) 89 | terms, whereas the |PsTerm| type is necessarily first-order. We 90 | mitigate this problem by allowing the conversion to `fail', by 91 | producing a term of the type |Exception|, as we saw in the 92 | introduction. 93 | 94 | Secondly, the |AgTerm| data type uses natural numbers to represent 95 | variables. The |PsTerm| data type, on the other hand, represents 96 | variables using a finite type |Fin n|, for some |n|. To convert 97 | between these representations, the function keeps track of the current 98 | depth, i.e.\ the number of |Π|-types it has encountered, and uses this 99 | information to ensure a correct conversion. We sketch the definition 100 | of the main function below: 101 | \begin{code} 102 | convert : (binders : ℕ) → AgTerm → Error (∃ PsTerm) 103 | convert b (var i []) = inj₂ (convertVar b i) 104 | convert b (con n args) = convertName n ∘ convert b ⟨$⟩ args 105 | convert b (def n args) = convertName n ∘ convert b ⟨$⟩ args 106 | convert b (pi (arg (arg-info visible _) (el _ t₁)) (el _ t₂)) 107 | with convert b t₁ | convert (suc b) t₂ 108 | ... | inj₁ msg | _ = inj₁ msg 109 | ... | _ | inj₁ msg = inj₁ msg 110 | ... | inj₂ (n₁ , p₁) | inj₂ (n₂ , p₂) 111 | with match p₁ p₂ 112 | ... | (p₁′ , p₂′) = inj₂ (n₁ ⊔ n₂ , con impl (p₁′ ∷ p₂′ ∷ [])) 113 | convert b (pi (arg _ _) (el _ t₂)) = convert (suc b) t₂ 114 | convert b _ = inj₁ unsupportedSyntax 115 | \end{code} 116 | We define special functions, |convertVar| and |name2term|, to convert 117 | variables and constructors or defined terms respectively. The 118 | arguments to constructors or defined terms are processed using the 119 | |convertChildren| function defined below. 120 | The conversion of a |pi| node binding an explicit argument proceeds by 121 | converting the domain and then codomain. If both conversions succeed, the 122 | resulting terms are |match|ed and a |PsTerm| is constructed using 123 | |impl|. 124 | Implicit arguments and instance arguments are ignored by this conversion 125 | function. Sorts, levels, or any other Agda feature mapped to the 126 | constructor |unknown| of type |Term| triggers a failure with the 127 | message |unsupportedSyntax|. 128 | 129 | The |convertChildren| function converts a list of |Term| arguments to a list 130 | of Prolog terms, by stripping the |arg| constructor and recursively 131 | applying the |convert| function. We only give its type signature 132 | here, as the definition is straightforward: 133 | \begin{code} 134 | convertChildren : ℕ → List (Arg Term) → Error (∃ (List ∘ PsTerm)) 135 | \end{code} 136 | % Next, the |convertName| function constructs a first-order constant from an 137 | % Agda |Name| and list of terms. 138 | % \begin{code} 139 | % convertName : Name → ∃ (λ n → List (PsTerm n)) → ∃ PsTerm 140 | % convertName f (n , ts) = n , con (name f) ts 141 | % \end{code} 142 | 143 | % Lastly, the |convertVar| function converts a natural number, 144 | % corresponding to a variable name in the |AgTerm| type, to the 145 | % corresponding |PsTerm|: 146 | % %{ 147 | % %format (dot (a)) = "\lfloor " a "\rfloor" 148 | % \begin{code} 149 | % convertVar : ℕ → ℕ → ∃ PsTerm 150 | % convertVar n i with compare n i 151 | % convertVar (dot( _)) _ | greater (dot(_)) k = (suc k , var (# k)) 152 | % convertVar (dot( _)) _ | equal (dot(_)) = (suc 0 , var (# 0)) 153 | % convertVar _ (dot( _)) | less (dot(_)) k = (0 , con (pvar k) []) 154 | % \end{code} 155 | % %} 156 | % The |convertVar| function compares the number of binders that have been 157 | % encountered with its argument De Bruijn index. If the variable is 158 | % bound within the goal type, it computes a corresponding |PsTerm| 159 | % variable; 160 | % if the variable is bound \emph{outside} of the goal type, however, we 161 | % compute a skolem constant. 162 | 163 | To convert between an |AgTerm| and |PsTerm| we simply call the 164 | |convert| function, initializing the number of binders 165 | encountered to |0|. 166 | \begin{code} 167 | agda2term : AgTerm → Error (∃ PsTerm) 168 | agda2term t = convert 0 t 169 | \end{code} 170 | 171 | 172 | \subsection*{Constructing rules} 173 | 174 | Our next goal is to construct rules. More specifically, we need to 175 | convert a list of quoted |Name|s to a hint database of Prolog rules. 176 | To return to our example in Section~\ref{sec:motivation}, the 177 | definition of |even+| had the following type: 178 | \begin{code} 179 | even+ : Even n → Even m → Even (n + m) 180 | \end{code} 181 | We would like to construct a value of type |Rule| that expresses how 182 | |even+| can be used. In Prolog, we might formulate the lemma above as 183 | the rule: 184 | \begin{verbatim} 185 | even(add(M,N)) :- even(M), even(N). 186 | \end{verbatim} 187 | In our Agda implementation, we can define such a rule manually: 188 | \begin{code} 189 | Even+ : Rule 2 190 | Even+ = record { 191 | name = name even+ 192 | conclusion = con (name (quote Even)) ( 193 | con (name (quote _+_)) (var (# 0) ∷ var (# 1) ∷ []) 194 | ∷ [] 195 | ) 196 | premises = con (name (quote Even)) (var (# 0) ∷ []) 197 | ∷ con (name (quote Even)) (var (# 1) ∷ []) 198 | ∷ [] 199 | } 200 | \end{code} 201 | In the coming subsection, we will show how to generate the above 202 | definition from the |Name| representing |even+|. 203 | 204 | This generation of rules is done in two steps. First, we will convert a 205 | |Name| to its corresponding |PsTerm|: 206 | \begin{code} 207 | name2term : Name → Error (∃ PsTerm) 208 | name2term = agda2term ∘ unel ∘ type 209 | \end{code} 210 | The |type| construct maps a |Name| to the |AgTerm| representing 211 | its type; the |unel| function discards any information about sorts; the 212 | |agda2term| was defined previously. 213 | 214 | In the next step, we process this |PsTerm|. The |split| 215 | function, defined below, splits a |PsTerm| at every top-most 216 | occurrence of the function symbol |impl|. Note that it would be 217 | possible to define this function directly on the |AgTerm| data type, 218 | but defining it on the |PsTerm| data type is much cleaner as we 219 | may assume that any unsupported syntax has already been removed. 220 | \begin{code} 221 | split : PsTerm n → ∃ (λ k → Vec (PsTerm n) (suc k)) 222 | split (con impl (t₁ ∷ t₂ ∷ [])) = Product.map suc (_∷_ t₁) (split t₂) 223 | split t = (0 , t ∷ []) 224 | \end{code} 225 | 226 | Using all these auxiliary functions, we now define 227 | the |name2rule| function below that constructs a |Rule| from an Agda |Name|. 228 | \begin{code} 229 | name2rule : Name → Error (∃ Rule) 230 | name2rule nm with name2term nm 231 | ... | inj₁ msg = inj₁ msg 232 | ... | inj₂ (n , t) with split t 233 | ... | (k , ts) with initLast ts 234 | ... | (prems , concl , _) = inj₂ (n , rule (name nm) concl (toList prems)) 235 | \end{code} 236 | We convert a name to its corresponding |PsTerm|, which is converted to 237 | a vector of terms using |split|. The last element of this vector is 238 | the conclusion of the rule; the prefix constitutes the premises. We 239 | use the |initLast| function from the Agda standard library, to 240 | decompose this vector accordingly. 241 | 242 | \subsection*{Constructing goals} 243 | 244 | Next, we turn our attention to converting a goal |AgTerm| to a 245 | |PsTerm|. While we could use the |agda2term| function to do so, 246 | there are good reasons to explore other alternatives. 247 | 248 | Consider the example given in Section~\ref{sec:motivation}. The goal 249 | |AgTerm| we wish to prove is |Even n → Even (n + 2)|. Calling 250 | |agda2term| would convert this to a |PsTerm|, where the function space 251 | has been replaced by the constructor |impl|. Instead, however, we 252 | would like to \emph{introduce} arguments, such as |Even n|, as 253 | assumptions to our hint database. 254 | 255 | In addition, we cannot directly reuse the implementation of |convert| 256 | that was used in the construction of terms. The |convert| function maps 257 | every |AgTerm| variable is mapped to a Prolog variable \emph{that may still be instantiated}. 258 | When considering the 259 | goal type, however, we want to generate \emph{skolem constants} for 260 | our variables. 261 | To account for this difference we have two flavours of the |convert| 262 | function: |convert| and |convert4Goal|. Both differ only in their 263 | implementation of |convertVar|. 264 | \begin{code} 265 | agda2goal×premises : AgTerm → Error (∃ PsTerm × HintDB) 266 | agda2goal×premises t with convert4Goal 0 t 267 | ... | inj₁ msg = inj₁ msg 268 | ... | inj₂ (n , p) with split p 269 | ... | (k , ts) with initLast ts 270 | ... | (prems , goal , _) = inj₂ ((n , goal) , toPremises k prems) 271 | \end{code} 272 | Fortunately, we can reuse many of the other functions we have defined 273 | above, and, using the |split| and |initLast| functions, we can get our 274 | hands on the list of premises |prems| and the desired return type 275 | |goal|. The only missing piece of the puzzle is a function, |toPremises|, 276 | which converts a list of |PsTerm|s to a hint database containing rules 277 | for the arguments of our goal. 278 | \begin{code} 279 | toPremises : ∀ {k} → ℕ → Vec (PsTerm n) k → HintDB 280 | toPremises i [] = [] 281 | toPremises i (t ∷ ts) = (n , rule (rvar i) t []) ∷ toPremises (suc i) ts 282 | \end{code} 283 | The |toPremises| converts every |PsTerm| in its argument list to a 284 | rule, using the argument's De Bruijn index as its rule name. 285 | 286 | \subsection*{Reification of proof terms} 287 | 288 | Now that we can compute Prolog terms, goals and rules from an Agda 289 | |Term|, we are ready to call the resolution mechanism described in 290 | Section~\ref{sec:prolog}. The only remaining problem is to convert the 291 | witness computed by our proof search back to an |AgTerm|, which can 292 | be unquoted to produce the desired proof. This is done by the 293 | |reify| function that traverses its argument |Proof|; the only 294 | interesting question is how it handles the variables and names it 295 | encounters. 296 | 297 | The |Proof| may contain two kinds of variables: locally bound 298 | variables, |rvar i|, or variables storing an Agda |Name|, |name n|. 299 | Each of these variables is treated differently in the |reify| 300 | function. 301 | \begin{code} 302 | reify : Proof → AgTerm 303 | reify (con (rvar i) ps) = var i [] 304 | reify (con (name n) ps) with definition n 305 | ... | function x = def n (toArg ∘ reify ⟨$⟩ ps) 306 | ... | constructor′ = con n (toArg ∘ reify ⟨$⟩ ps) 307 | ... | _ = unknown 308 | where 309 | toArg : AgTerm → Arg AgTerm 310 | toArg = arg (arg-info visible relevant) 311 | \end{code} 312 | Any references to locally bound variables are mapped to the |var| 313 | constructor of the |AgTerm| data type. These variables correspond 314 | to usage of arguments to the function being defined. As we know by 315 | construction that these arguments are mapped to rules without 316 | premises, the corresponding Agda variables do not need any further 317 | arguments. 318 | 319 | If, on the other hand, the rule being applied is constructed using a |name|, we do 320 | disambiguate whether the rule name refers to a function or a 321 | constructor. The |definition| function, defined in Agda's reflection 322 | library, tells you how a name was defined (i.e. as a function name, 323 | constructor, etc). For the sake of brevity, we restrict 324 | the definition here to only handle defined functions and data 325 | constructors. It is easy enough to extend with further branches for 326 | postulates, primitives, and so forth. 327 | 328 | We will also need to wrap additional lambdas around the resulting 329 | term, due to the premises that were introduced by the 330 | |agda2goal×premises| function. 331 | To do so, we define the |intros| function that repeatedly wraps its 332 | argument term in a lambda. 333 | \begin{code} 334 | intros : AgTerm → AgTerm 335 | intros = introsAcc (length args) 336 | where 337 | introsAcc : ℕ → AgTerm → AgTerm 338 | introsAcc zero t = t 339 | introsAcc (suc k) t = lam visible (introsAcc k t) 340 | \end{code} 341 | 342 | \subsection*{Hint databases} 343 | \label{sec:hintdbs} 344 | 345 | Users to provide hints, i.e., rules that may be used during 346 | resolution, in the form of a \emph{hint database}. These hint 347 | databases consist of an (existentially quantified) a list of rules. 348 | We can add new hints to an existing database using the insertion 349 | operator, |<<|, defined as follows: 350 | \begin{code} 351 | _<<_ : HintDB → Name → HintDB 352 | db << n with name2rule n 353 | db << n | inj₁ msg = db 354 | db << n | inj₂ r = db ++ [ r ] 355 | \end{code} 356 | If the generation of a rule fails for whatever reason, no error is 357 | raised, and the rule is simply ignored. Our actual implementation 358 | requires an implicit proof argument that all the names in the argument 359 | list can be quoted successfully. If you define such proofs to compute 360 | the trivial unit record as evidence, Agda will fill them in 361 | automatically in every call to the |_<<_| function on constant 362 | arguments. This simple form of proof automation is pervasive in Agda 363 | programs~\citep{oury,swierstra-more}. 364 | 365 | This is the simplest possible form of hint database. In principle, 366 | there is no reason not to define alternative versions that assign 367 | priorities to certain rules or limit the number of times a rule may be 368 | applied. We will investigate some possibilities for extensible proof 369 | search in section~\ref{sec:extensible}. 370 | 371 | It is worth repeating that hint databases are first-class objects. We 372 | can combine hints databases, filter certain rules from a hint 373 | database, or manipulate them in any way we wish. 374 | 375 | \subsection*{Error messages} 376 | Lastly, we need to decide how to report error messages. Since we are 377 | going to return an |AgTerm|, we need to transform the |Message| 378 | type we saw previously into an |AgTerm|. When unquoted, this term 379 | will cause a type error, reporting the reason for failure. To 380 | accomplish this, we introduce a dependent type, indexed by a |Message|: 381 | \begin{code} 382 | data Exception : Message → Set where 383 | throw : (msg : Message) → Exception msg 384 | \end{code} 385 | The message passed as an argument to the |throw| constructor, will be 386 | recorded in the |Exception|'s type, as we intended. 387 | 388 | Next, we define a function to produce an |AgTerm| from a 389 | |Message|. We could construct such terms by hand, but it is easier to 390 | just use Agda's |quoteTerm| construct: 391 | \begin{code} 392 | quoteError : Message → Term 393 | quoteError searchSpaceExhausted = 394 | quoteTerm (throw searchSpaceExhausted) 395 | quoteError unsupportedSyntax = 396 | quoteTerm (throw unsupportedSyntax) 397 | \end{code} 398 | 399 | \subsection*{Putting it all together} 400 | 401 | Finally, we can present the definition of the |auto| function used in 402 | the examples in Section~\ref{sec:motivation}: 403 | \begin{code} 404 | auto : ℕ → HintDB → AgTerm → AgTerm 405 | auto depth rules goalType 406 | with agda2goal×premises goalType 407 | ... | inj₁ msg = quoteError msg 408 | ... | inj₂ ((n , g) , args) 409 | with dfs depth (solve g (args ++ rules)) 410 | ... | [] = quoteError searchSpaceExhausted 411 | ... | (p ∷ _) = intros (reify p) 412 | \end{code} 413 | The |auto| function takes an |AgTerm| representing the goal type, 414 | splits it into |PsTerm|s representing the goal |g| and a list of 415 | arguments, |args|. These arguments are added to the initial hint 416 | database. Calling the |solve| function with this hint database and the 417 | goal |g|, constructs a proof tree, that we traverse up to the given 418 | |depth| in search of a solution. If this proof search succeeds, the 419 | |Proof| is converted to an |AgTerm|, a witness that the original goal 420 | is inhabited. There are two places where this function may fail: the 421 | conversion to a |PsTerm| may fail because of unsupported syntax; or 422 | the proof search may not find a result. 423 | 424 | 425 | %%% Local Variables: 426 | %%% mode: latex 427 | %%% TeX-master: t 428 | %%% TeX-command-default: "rake" 429 | %%% End: 430 | -------------------------------------------------------------------------------- /doc/sigplanconf.cls: -------------------------------------------------------------------------------- 1 | %----------------------------------------------------------------------------- 2 | % 3 | % LaTeX Class/Style File 4 | % 5 | % Name: sigplanconf.cls 6 | % 7 | % Purpose: A LaTeX 2e class file for SIGPLAN conference proceedings. 8 | % This class file supercedes acm_proc_article-sp, 9 | % sig-alternate, and sigplan-proc. 10 | % 11 | % Author: Paul C. Anagnostopoulos 12 | % Windfall Software 13 | % 978 371-2316 14 | % paul [atsign] windfall.com 15 | % 16 | % Created: 12 September 2004 17 | % 18 | % Revisions: See end of file. 19 | % 20 | % This work is licensed under the Creative Commons Attribution License. 21 | % To view a copy of this license, visit 22 | % http://creativecommons.org/licenses/by/3.0/ 23 | % or send a letter to Creative Commons, 171 2nd Street, Suite 300, 24 | % San Francisco, California, 94105, U.S.A. 25 | % 26 | %----------------------------------------------------------------------------- 27 | 28 | 29 | \NeedsTeXFormat{LaTeX2e}[1995/12/01] 30 | \ProvidesClass{sigplanconf}[2011/11/08 v2.5 ACM SIGPLAN Proceedings] 31 | 32 | % The following few pages contain LaTeX programming extensions adapted 33 | % from the ZzTeX macro package. 34 | 35 | % Token Hackery 36 | % ----- ------- 37 | 38 | 39 | \def \@expandaftertwice {\expandafter\expandafter\expandafter} 40 | \def \@expandafterthrice {\expandafter\expandafter\expandafter\expandafter 41 | \expandafter\expandafter\expandafter} 42 | 43 | % This macro discards the next token. 44 | 45 | \def \@discardtok #1{}% token 46 | 47 | % This macro removes the `pt' following a dimension. 48 | 49 | {\catcode `\p = 12 \catcode `\t = 12 50 | 51 | \gdef \@remover #1pt{#1} 52 | 53 | } % \catcode 54 | 55 | % This macro extracts the contents of a macro and returns it as plain text. 56 | % Usage: \expandafter\@defof \meaning\macro\@mark 57 | 58 | \def \@defof #1:->#2\@mark{#2} 59 | 60 | % Control Sequence Names 61 | % ------- -------- ----- 62 | 63 | 64 | \def \@name #1{% {\tokens} 65 | \csname \expandafter\@discardtok \string#1\endcsname} 66 | 67 | \def \@withname #1#2{% {\command}{\tokens} 68 | \expandafter#1\csname \expandafter\@discardtok \string#2\endcsname} 69 | 70 | % Flags (Booleans) 71 | % ----- ---------- 72 | 73 | % The boolean literals \@true and \@false are appropriate for use with 74 | % the \if command, which tests the codes of the next two characters. 75 | 76 | \def \@true {TT} 77 | \def \@false {FL} 78 | 79 | \def \@setflag #1=#2{\edef #1{#2}}% \flag = boolean 80 | 81 | % IF and Predicates 82 | % -- --- ---------- 83 | 84 | % A "predicate" is a macro that returns \@true or \@false as its value. 85 | % Such values are suitable for use with the \if conditional. For example: 86 | % 87 | % \if \@oddp{\x} \else \fi 88 | 89 | % A predicate can be used with \@setflag as follows: 90 | % 91 | % \@setflag \flag = {} 92 | 93 | % Here are the predicates for TeX's repertoire of conditional 94 | % commands. These might be more appropriately interspersed with 95 | % other definitions in this module, but what the heck. 96 | % Some additional "obvious" predicates are defined. 97 | 98 | \def \@eqlp #1#2{\ifnum #1 = #2\@true \else \@false \fi} 99 | \def \@neqlp #1#2{\ifnum #1 = #2\@false \else \@true \fi} 100 | \def \@lssp #1#2{\ifnum #1 < #2\@true \else \@false \fi} 101 | \def \@gtrp #1#2{\ifnum #1 > #2\@true \else \@false \fi} 102 | \def \@zerop #1{\ifnum #1 = 0\@true \else \@false \fi} 103 | \def \@onep #1{\ifnum #1 = 1\@true \else \@false \fi} 104 | \def \@posp #1{\ifnum #1 > 0\@true \else \@false \fi} 105 | \def \@negp #1{\ifnum #1 < 0\@true \else \@false \fi} 106 | \def \@oddp #1{\ifodd #1\@true \else \@false \fi} 107 | \def \@evenp #1{\ifodd #1\@false \else \@true \fi} 108 | \def \@rangep #1#2#3{\if \@orp{\@lssp{#1}{#2}}{\@gtrp{#1}{#3}}\@false \else 109 | \@true \fi} 110 | \def \@tensp #1{\@rangep{#1}{10}{19}} 111 | 112 | \def \@dimeqlp #1#2{\ifdim #1 = #2\@true \else \@false \fi} 113 | \def \@dimneqlp #1#2{\ifdim #1 = #2\@false \else \@true \fi} 114 | \def \@dimlssp #1#2{\ifdim #1 < #2\@true \else \@false \fi} 115 | \def \@dimgtrp #1#2{\ifdim #1 > #2\@true \else \@false \fi} 116 | \def \@dimzerop #1{\ifdim #1 = 0pt\@true \else \@false \fi} 117 | \def \@dimposp #1{\ifdim #1 > 0pt\@true \else \@false \fi} 118 | \def \@dimnegp #1{\ifdim #1 < 0pt\@true \else \@false \fi} 119 | 120 | \def \@vmodep {\ifvmode \@true \else \@false \fi} 121 | \def \@hmodep {\ifhmode \@true \else \@false \fi} 122 | \def \@mathmodep {\ifmmode \@true \else \@false \fi} 123 | \def \@textmodep {\ifmmode \@false \else \@true \fi} 124 | \def \@innermodep {\ifinner \@true \else \@false \fi} 125 | 126 | \long\def \@codeeqlp #1#2{\if #1#2\@true \else \@false \fi} 127 | 128 | \long\def \@cateqlp #1#2{\ifcat #1#2\@true \else \@false \fi} 129 | 130 | \long\def \@tokeqlp #1#2{\ifx #1#2\@true \else \@false \fi} 131 | \long\def \@xtokeqlp #1#2{\expandafter\ifx #1#2\@true \else \@false \fi} 132 | 133 | \long\def \@definedp #1{% 134 | \expandafter\ifx \csname \expandafter\@discardtok \string#1\endcsname 135 | \relax \@false \else \@true \fi} 136 | 137 | \long\def \@undefinedp #1{% 138 | \expandafter\ifx \csname \expandafter\@discardtok \string#1\endcsname 139 | \relax \@true \else \@false \fi} 140 | 141 | \def \@emptydefp #1{\ifx #1\@empty \@true \else \@false \fi}% {\name} 142 | 143 | \let \@emptylistp = \@emptydefp 144 | 145 | \long\def \@emptyargp #1{% {#n} 146 | \@empargp #1\@empargq\@mark} 147 | \long\def \@empargp #1#2\@mark{% 148 | \ifx #1\@empargq \@true \else \@false \fi} 149 | \def \@empargq {\@empargq} 150 | 151 | \def \@emptytoksp #1{% {\tokenreg} 152 | \expandafter\@emptoksp \the#1\@mark} 153 | 154 | \long\def \@emptoksp #1\@mark{\@emptyargp{#1}} 155 | 156 | \def \@voidboxp #1{\ifvoid #1\@true \else \@false \fi} 157 | \def \@hboxp #1{\ifhbox #1\@true \else \@false \fi} 158 | \def \@vboxp #1{\ifvbox #1\@true \else \@false \fi} 159 | 160 | \def \@eofp #1{\ifeof #1\@true \else \@false \fi} 161 | 162 | 163 | % Flags can also be used as predicates, as in: 164 | % 165 | % \if \flaga \else \fi 166 | 167 | 168 | % Now here we have predicates for the common logical operators. 169 | 170 | \def \@notp #1{\if #1\@false \else \@true \fi} 171 | 172 | \def \@andp #1#2{\if #1% 173 | \if #2\@true \else \@false \fi 174 | \else 175 | \@false 176 | \fi} 177 | 178 | \def \@orp #1#2{\if #1% 179 | \@true 180 | \else 181 | \if #2\@true \else \@false \fi 182 | \fi} 183 | 184 | \def \@xorp #1#2{\if #1% 185 | \if #2\@false \else \@true \fi 186 | \else 187 | \if #2\@true \else \@false \fi 188 | \fi} 189 | 190 | % Arithmetic 191 | % ---------- 192 | 193 | \def \@increment #1{\advance #1 by 1\relax}% {\count} 194 | 195 | \def \@decrement #1{\advance #1 by -1\relax}% {\count} 196 | 197 | % Options 198 | % ------- 199 | 200 | 201 | \@setflag \@authoryear = \@false 202 | \@setflag \@blockstyle = \@false 203 | \@setflag \@copyrightwanted = \@true 204 | \@setflag \@explicitsize = \@false 205 | \@setflag \@mathtime = \@false 206 | \@setflag \@natbib = \@true 207 | \@setflag \@ninepoint = \@true 208 | \newcount{\@numheaddepth} \@numheaddepth = 3 209 | \@setflag \@onecolumn = \@false 210 | \@setflag \@preprint = \@false 211 | \@setflag \@reprint = \@false 212 | \@setflag \@tenpoint = \@false 213 | \@setflag \@times = \@false 214 | 215 | % Note that all the dangerous article class options are trapped. 216 | 217 | \DeclareOption{9pt}{\@setflag \@ninepoint = \@true 218 | \@setflag \@explicitsize = \@true} 219 | 220 | \DeclareOption{10pt}{\PassOptionsToClass{10pt}{article}% 221 | \@setflag \@ninepoint = \@false 222 | \@setflag \@tenpoint = \@true 223 | \@setflag \@explicitsize = \@true} 224 | 225 | \DeclareOption{11pt}{\PassOptionsToClass{11pt}{article}% 226 | \@setflag \@ninepoint = \@false 227 | \@setflag \@explicitsize = \@true} 228 | 229 | \DeclareOption{12pt}{\@unsupportedoption{12pt}} 230 | 231 | \DeclareOption{a4paper}{\@unsupportedoption{a4paper}} 232 | 233 | \DeclareOption{a5paper}{\@unsupportedoption{a5paper}} 234 | 235 | \DeclareOption{authoryear}{\@setflag \@authoryear = \@true} 236 | 237 | \DeclareOption{b5paper}{\@unsupportedoption{b5paper}} 238 | 239 | \DeclareOption{blockstyle}{\@setflag \@blockstyle = \@true} 240 | 241 | \DeclareOption{cm}{\@setflag \@times = \@false} 242 | 243 | \DeclareOption{computermodern}{\@setflag \@times = \@false} 244 | 245 | \DeclareOption{executivepaper}{\@unsupportedoption{executivepaper}} 246 | 247 | \DeclareOption{indentedstyle}{\@setflag \@blockstyle = \@false} 248 | 249 | \DeclareOption{landscape}{\@unsupportedoption{landscape}} 250 | 251 | \DeclareOption{legalpaper}{\@unsupportedoption{legalpaper}} 252 | 253 | \DeclareOption{letterpaper}{\@unsupportedoption{letterpaper}} 254 | 255 | \DeclareOption{mathtime}{\@setflag \@mathtime = \@true} 256 | 257 | \DeclareOption{natbib}{\@setflag \@natbib = \@true} 258 | 259 | \DeclareOption{nonatbib}{\@setflag \@natbib = \@false} 260 | 261 | \DeclareOption{nocopyrightspace}{\@setflag \@copyrightwanted = \@false} 262 | 263 | \DeclareOption{notitlepage}{\@unsupportedoption{notitlepage}} 264 | 265 | \DeclareOption{numberedpars}{\@numheaddepth = 4} 266 | 267 | \DeclareOption{numbers}{\@setflag \@authoryear = \@false} 268 | 269 | %%%\DeclareOption{onecolumn}{\@setflag \@onecolumn = \@true} 270 | 271 | \DeclareOption{preprint}{\@setflag \@preprint = \@true} 272 | 273 | \DeclareOption{reprint}{\@setflag \@reprint = \@true} 274 | 275 | \DeclareOption{times}{\@setflag \@times = \@true} 276 | 277 | \DeclareOption{titlepage}{\@unsupportedoption{titlepage}} 278 | 279 | \DeclareOption{twocolumn}{\@setflag \@onecolumn = \@false} 280 | 281 | \DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}} 282 | 283 | \ExecuteOptions{9pt,indentedstyle,times} 284 | \@setflag \@explicitsize = \@false 285 | \ProcessOptions 286 | 287 | \if \@onecolumn 288 | \if \@notp{\@explicitsize}% 289 | \@setflag \@ninepoint = \@false 290 | \PassOptionsToClass{11pt}{article}% 291 | \fi 292 | \PassOptionsToClass{twoside,onecolumn}{article} 293 | \else 294 | \PassOptionsToClass{twoside,twocolumn}{article} 295 | \fi 296 | \LoadClass{article} 297 | 298 | \def \@unsupportedoption #1{% 299 | \ClassError{proc}{The standard '#1' option is not supported.}} 300 | 301 | % This can be used with the 'reprint' option to get the final folios. 302 | 303 | \def \setpagenumber #1{% 304 | \setcounter{page}{#1}} 305 | 306 | \AtEndDocument{\label{sigplanconf@finalpage}} 307 | 308 | % Utilities 309 | % --------- 310 | 311 | 312 | \newcommand{\setvspace}[2]{% 313 | #1 = #2 314 | \advance #1 by -1\parskip} 315 | 316 | % Document Parameters 317 | % -------- ---------- 318 | 319 | 320 | % Page: 321 | 322 | \setlength{\hoffset}{-1in} 323 | \setlength{\voffset}{-1in} 324 | 325 | \setlength{\topmargin}{1in} 326 | \setlength{\headheight}{0pt} 327 | \setlength{\headsep}{0pt} 328 | 329 | \if \@onecolumn 330 | \setlength{\evensidemargin}{.75in} 331 | \setlength{\oddsidemargin}{.75in} 332 | \else 333 | \setlength{\evensidemargin}{.75in} 334 | \setlength{\oddsidemargin}{.75in} 335 | \fi 336 | 337 | % Text area: 338 | 339 | \newdimen{\standardtextwidth} 340 | \setlength{\standardtextwidth}{42pc} 341 | 342 | \if \@onecolumn 343 | \setlength{\textwidth}{40.5pc} 344 | \else 345 | \setlength{\textwidth}{\standardtextwidth} 346 | \fi 347 | 348 | \setlength{\topskip}{8pt} 349 | \setlength{\columnsep}{2pc} 350 | \setlength{\textheight}{54.5pc} 351 | 352 | % Running foot: 353 | 354 | \setlength{\footskip}{30pt} 355 | 356 | % Paragraphs: 357 | 358 | \if \@blockstyle 359 | \setlength{\parskip}{5pt plus .1pt minus .5pt} 360 | \setlength{\parindent}{0pt} 361 | \else 362 | \setlength{\parskip}{0pt} 363 | \setlength{\parindent}{12pt} 364 | \fi 365 | 366 | \setlength{\lineskip}{.5pt} 367 | \setlength{\lineskiplimit}{\lineskip} 368 | 369 | \frenchspacing 370 | \pretolerance = 400 371 | \tolerance = \pretolerance 372 | \setlength{\emergencystretch}{5pt} 373 | \clubpenalty = 10000 374 | \widowpenalty = 10000 375 | \setlength{\hfuzz}{.5pt} 376 | 377 | % Standard vertical spaces: 378 | 379 | \newskip{\standardvspace} 380 | \setvspace{\standardvspace}{5pt plus 1pt minus .5pt} 381 | 382 | % Margin paragraphs: 383 | 384 | \setlength{\marginparwidth}{36pt} 385 | \setlength{\marginparsep}{2pt} 386 | \setlength{\marginparpush}{8pt} 387 | 388 | 389 | \setlength{\skip\footins}{8pt plus 3pt minus 1pt} 390 | \setlength{\footnotesep}{9pt} 391 | 392 | \renewcommand{\footnoterule}{% 393 | \hrule width .5\columnwidth height .33pt depth 0pt} 394 | 395 | \renewcommand{\@makefntext}[1]{% 396 | \noindent \@makefnmark \hspace{1pt}#1} 397 | 398 | % Floats: 399 | 400 | \setcounter{topnumber}{4} 401 | \setcounter{bottomnumber}{1} 402 | \setcounter{totalnumber}{4} 403 | 404 | \renewcommand{\fps@figure}{tp} 405 | \renewcommand{\fps@table}{tp} 406 | \renewcommand{\topfraction}{0.90} 407 | \renewcommand{\bottomfraction}{0.30} 408 | \renewcommand{\textfraction}{0.10} 409 | \renewcommand{\floatpagefraction}{0.75} 410 | 411 | \setcounter{dbltopnumber}{4} 412 | 413 | \renewcommand{\dbltopfraction}{\topfraction} 414 | \renewcommand{\dblfloatpagefraction}{\floatpagefraction} 415 | 416 | \setlength{\floatsep}{18pt plus 4pt minus 2pt} 417 | \setlength{\textfloatsep}{18pt plus 4pt minus 3pt} 418 | \setlength{\intextsep}{10pt plus 4pt minus 3pt} 419 | 420 | \setlength{\dblfloatsep}{18pt plus 4pt minus 2pt} 421 | \setlength{\dbltextfloatsep}{20pt plus 4pt minus 3pt} 422 | 423 | % Miscellaneous: 424 | 425 | \errorcontextlines = 5 426 | 427 | % Fonts 428 | % ----- 429 | 430 | 431 | \if \@times 432 | \renewcommand{\rmdefault}{ptm}% 433 | \if \@mathtime 434 | \usepackage[mtbold,noTS1]{mathtime}% 435 | \else 436 | %%% \usepackage{mathptm}% 437 | \fi 438 | \else 439 | \relax 440 | \fi 441 | 442 | \if \@ninepoint 443 | 444 | \renewcommand{\normalsize}{% 445 | \@setfontsize{\normalsize}{9pt}{10pt}% 446 | \setlength{\abovedisplayskip}{5pt plus 1pt minus .5pt}% 447 | \setlength{\belowdisplayskip}{\abovedisplayskip}% 448 | \setlength{\abovedisplayshortskip}{3pt plus 1pt minus 2pt}% 449 | \setlength{\belowdisplayshortskip}{\abovedisplayshortskip}} 450 | 451 | \renewcommand{\tiny}{\@setfontsize{\tiny}{5pt}{6pt}} 452 | 453 | \renewcommand{\scriptsize}{\@setfontsize{\scriptsize}{7pt}{8pt}} 454 | 455 | \renewcommand{\small}{% 456 | \@setfontsize{\small}{8pt}{9pt}% 457 | \setlength{\abovedisplayskip}{4pt plus 1pt minus 1pt}% 458 | \setlength{\belowdisplayskip}{\abovedisplayskip}% 459 | \setlength{\abovedisplayshortskip}{2pt plus 1pt}% 460 | \setlength{\belowdisplayshortskip}{\abovedisplayshortskip}} 461 | 462 | \renewcommand{\footnotesize}{% 463 | \@setfontsize{\footnotesize}{8pt}{9pt}% 464 | \setlength{\abovedisplayskip}{4pt plus 1pt minus .5pt}% 465 | \setlength{\belowdisplayskip}{\abovedisplayskip}% 466 | \setlength{\abovedisplayshortskip}{2pt plus 1pt}% 467 | \setlength{\belowdisplayshortskip}{\abovedisplayshortskip}} 468 | 469 | \renewcommand{\large}{\@setfontsize{\large}{11pt}{13pt}} 470 | 471 | \renewcommand{\Large}{\@setfontsize{\Large}{14pt}{18pt}} 472 | 473 | \renewcommand{\LARGE}{\@setfontsize{\LARGE}{18pt}{20pt}} 474 | 475 | \renewcommand{\huge}{\@setfontsize{\huge}{20pt}{25pt}} 476 | 477 | \renewcommand{\Huge}{\@setfontsize{\Huge}{25pt}{30pt}} 478 | 479 | \else\if \@tenpoint 480 | 481 | \relax 482 | 483 | \else 484 | 485 | \relax 486 | 487 | \fi\fi 488 | 489 | % Abstract 490 | % -------- 491 | 492 | 493 | \renewenvironment{abstract}{% 494 | \section*{Abstract}% 495 | \normalsize}{% 496 | } 497 | 498 | % Bibliography 499 | % ------------ 500 | 501 | 502 | \renewenvironment{thebibliography}[1] 503 | {\section*{\refname 504 | \@mkboth{\MakeUppercase\refname}{\MakeUppercase\refname}}% 505 | \list{\@biblabel{\@arabic\c@enumiv}}% 506 | {\settowidth\labelwidth{\@biblabel{#1}}% 507 | \leftmargin\labelwidth 508 | \advance\leftmargin\labelsep 509 | \@openbib@code 510 | \usecounter{enumiv}% 511 | \let\p@enumiv\@empty 512 | \renewcommand\theenumiv{\@arabic\c@enumiv}}% 513 | \bibfont 514 | \clubpenalty4000 515 | \@clubpenalty \clubpenalty 516 | \widowpenalty4000% 517 | \sfcode`\.\@m} 518 | {\def\@noitemerr 519 | {\@latex@warning{Empty `thebibliography' environment}}% 520 | \endlist} 521 | 522 | \if \@natbib 523 | 524 | \if \@authoryear 525 | \typeout{Using natbib package with 'authoryear' citation style.} 526 | \usepackage[authoryear,square]{natbib} 527 | \bibpunct{[}{]}{;}{a}{}{,} % Change citation separator to semicolon, 528 | % eliminate comma between author and year. 529 | \let \cite = \citep 530 | \else 531 | \typeout{Using natbib package with 'numbers' citation style.} 532 | \usepackage[numbers,sort&compress,square]{natbib} 533 | \fi 534 | \setlength{\bibsep}{3pt plus .5pt minus .25pt} 535 | 536 | \fi 537 | 538 | \def \bibfont {\small} 539 | 540 | % Categories 541 | % ---------- 542 | 543 | 544 | \@setflag \@firstcategory = \@true 545 | 546 | \newcommand{\category}[3]{% 547 | \if \@firstcategory 548 | \paragraph*{Categories and Subject Descriptors}% 549 | \@setflag \@firstcategory = \@false 550 | \else 551 | \unskip ;\hspace{.75em}% 552 | \fi 553 | \@ifnextchar [{\@category{#1}{#2}{#3}}{\@category{#1}{#2}{#3}[]}} 554 | 555 | \def \@category #1#2#3[#4]{% 556 | {\let \and = \relax 557 | #1 [\textit{#2}]% 558 | \if \@emptyargp{#4}% 559 | \if \@notp{\@emptyargp{#3}}: #3\fi 560 | \else 561 | :\space 562 | \if \@notp{\@emptyargp{#3}}#3---\fi 563 | \textrm{#4}% 564 | \fi}} 565 | 566 | % Copyright Notice 567 | % --------- ------ 568 | 569 | 570 | \def \ftype@copyrightbox {8} 571 | \def \@toappear {} 572 | \def \@permission {} 573 | \def \@reprintprice {} 574 | 575 | \def \@copyrightspace {% 576 | \@float{copyrightbox}[b]% 577 | \vbox to 1in{% 578 | \vfill 579 | \parbox[b]{20pc}{% 580 | \scriptsize 581 | \if \@preprint 582 | [Copyright notice will appear here 583 | once 'preprint' option is removed.]\par 584 | \else 585 | \@toappear 586 | \fi 587 | \if \@reprint 588 | \noindent Reprinted from \@conferencename, 589 | \@proceedings, 590 | \@conferenceinfo, 591 | pp.~\number\thepage--\pageref{sigplanconf@finalpage}.\par 592 | \fi}}% 593 | \end@float} 594 | 595 | \long\def \toappear #1{% 596 | \def \@toappear {#1}} 597 | 598 | \toappear{% 599 | \noindent \@permission \par 600 | \vspace{2pt} 601 | \noindent \textsl{\@conferencename}\quad \@conferenceinfo \par 602 | \noindent Copyright \copyright\ \@copyrightyear\ ACM \@copyrightdata 603 | \dots \@reprintprice\par} 604 | 605 | \newcommand{\permission}[1]{% 606 | \gdef \@permission {#1}} 607 | 608 | \permission{% 609 | Permission to make digital or hard copies of all or 610 | part of this work for personal or classroom use is granted without 611 | fee provided that copies are not made or distributed for profit or 612 | commercial advantage and that copies bear this notice and the full 613 | citation on the first page. To copy otherwise, to republish, to 614 | post on servers or to redistribute to lists, requires prior specific 615 | permission and/or a fee.} 616 | 617 | % Here we have some alternate permission statements and copyright lines: 618 | 619 | \newcommand{\ACMCanadapermission}{% 620 | \permission{% 621 | Copyright \@copyrightyear\ Association for Computing Machinery. 622 | ACM acknowledges that 623 | this contribution was authored or co-authored by an affiliate of the 624 | National Research Council of Canada (NRC). 625 | As such, the Crown in Right of 626 | Canada retains an equal interest in the copyright, however granting 627 | nonexclusive, royalty-free right to publish or reproduce this article, 628 | or to allow others to do so, provided that clear attribution 629 | is also given to the authors and the NRC.}} 630 | 631 | \newcommand{\ACMUSpermission}{% 632 | \permission{% 633 | Copyright \@copyrightyear\ Association for 634 | Computing Machinery. ACM acknowledges that 635 | this contribution was authored or co-authored 636 | by a contractor or affiliate 637 | of the U.S. Government. As such, the Government retains a nonexclusive, 638 | royalty-free right to publish or reproduce this article, 639 | or to allow others to do so, for Government purposes only.}} 640 | 641 | \newcommand{\authorpermission}{% 642 | \permission{% 643 | Copyright is held by the author/owner(s).} 644 | \toappear{% 645 | \noindent \@permission \par 646 | \vspace{2pt} 647 | \noindent \textsl{\@conferencename}\quad \@conferenceinfo \par 648 | ACM \@copyrightdata.}} 649 | 650 | \newcommand{\Sunpermission}{% 651 | \permission{% 652 | Copyright is held by Sun Microsystems, Inc.}% 653 | \toappear{% 654 | \noindent \@permission \par 655 | \vspace{2pt} 656 | \noindent \textsl{\@conferencename}\quad \@conferenceinfo \par 657 | ACM \@copyrightdata.}} 658 | 659 | \newcommand{\USpublicpermission}{% 660 | \permission{% 661 | This paper is authored by an employee(s) of the United States 662 | Government and is in the public domain.}% 663 | \toappear{% 664 | \noindent \@permission \par 665 | \vspace{2pt} 666 | \noindent \textsl{\@conferencename}\quad \@conferenceinfo \par 667 | ACM \@copyrightdata.}} 668 | 669 | \newcommand{\reprintprice}[1]{% 670 | \gdef \@reprintprice {#1}} 671 | 672 | \reprintprice{\$10.00} 673 | 674 | \newcommand{\authorversion}[4]{% 675 | \permission{% 676 | Copyright \copyright\ ACM, #1. This is the author's version of the work. 677 | It is posted here by permission of ACM for your personal use. 678 | Not for redistribution. The definitive version was published in 679 | #2, #3, http://doi.acm.org/10.1145/#4.}} 680 | 681 | % Enunciations 682 | % ------------ 683 | 684 | 685 | \def \@begintheorem #1#2{% {name}{number} 686 | \trivlist 687 | \item[\hskip \labelsep \textsc{#1 #2.}]% 688 | \itshape\selectfont 689 | \ignorespaces} 690 | 691 | \def \@opargbegintheorem #1#2#3{% {name}{number}{title} 692 | \trivlist 693 | \item[% 694 | \hskip\labelsep \textsc{#1\ #2}% 695 | \if \@notp{\@emptyargp{#3}}\nut (#3).\fi]% 696 | \itshape\selectfont 697 | \ignorespaces} 698 | 699 | % Figures 700 | % ------- 701 | 702 | 703 | \@setflag \@caprule = \@true 704 | 705 | \long\def \@makecaption #1#2{% 706 | \addvspace{4pt} 707 | \if \@caprule 708 | \hrule width \hsize height .33pt 709 | \vspace{4pt} 710 | \fi 711 | \setbox \@tempboxa = \hbox{\@setfigurenumber{#1.}\nut #2}% 712 | \if \@dimgtrp{\wd\@tempboxa}{\hsize}% 713 | \noindent \@setfigurenumber{#1.}\nut #2\par 714 | \else 715 | \centerline{\box\@tempboxa}% 716 | \fi} 717 | 718 | \newcommand{\nocaptionrule}{% 719 | \@setflag \@caprule = \@false} 720 | 721 | \def \@setfigurenumber #1{% 722 | {\rmfamily \bfseries \selectfont #1}} 723 | 724 | % Hierarchy 725 | % --------- 726 | 727 | 728 | \setcounter{secnumdepth}{\@numheaddepth} 729 | 730 | \newskip{\@sectionaboveskip} 731 | \setvspace{\@sectionaboveskip}{10pt plus 3pt minus 2pt} 732 | 733 | \newskip{\@sectionbelowskip} 734 | \if \@blockstyle 735 | \setlength{\@sectionbelowskip}{0.1pt}% 736 | \else 737 | \setlength{\@sectionbelowskip}{4pt}% 738 | \fi 739 | 740 | \renewcommand{\section}{% 741 | \@startsection 742 | {section}% 743 | {1}% 744 | {0pt}% 745 | {-\@sectionaboveskip}% 746 | {\@sectionbelowskip}% 747 | {\large \bfseries \raggedright}} 748 | 749 | \newskip{\@subsectionaboveskip} 750 | \setvspace{\@subsectionaboveskip}{8pt plus 2pt minus 2pt} 751 | 752 | \newskip{\@subsectionbelowskip} 753 | \if \@blockstyle 754 | \setlength{\@subsectionbelowskip}{0.1pt}% 755 | \else 756 | \setlength{\@subsectionbelowskip}{4pt}% 757 | \fi 758 | 759 | \renewcommand{\subsection}{% 760 | \@startsection% 761 | {subsection}% 762 | {2}% 763 | {0pt}% 764 | {-\@subsectionaboveskip}% 765 | {\@subsectionbelowskip}% 766 | {\normalsize \bfseries \raggedright}} 767 | 768 | \renewcommand{\subsubsection}{% 769 | \@startsection% 770 | {subsubsection}% 771 | {3}% 772 | {0pt}% 773 | {-\@subsectionaboveskip} 774 | {\@subsectionbelowskip}% 775 | {\normalsize \bfseries \raggedright}} 776 | 777 | \newskip{\@paragraphaboveskip} 778 | \setvspace{\@paragraphaboveskip}{6pt plus 2pt minus 2pt} 779 | 780 | \renewcommand{\paragraph}{% 781 | \@startsection% 782 | {paragraph}% 783 | {4}% 784 | {0pt}% 785 | {\@paragraphaboveskip} 786 | {-1em}% 787 | {\normalsize \bfseries \if \@times \itshape \fi}} 788 | 789 | \renewcommand{\subparagraph}{% 790 | \@startsection% 791 | {subparagraph}% 792 | {4}% 793 | {0pt}% 794 | {\@paragraphaboveskip} 795 | {-1em}% 796 | {\normalsize \itshape}} 797 | 798 | % Standard headings: 799 | 800 | \newcommand{\acks}{\section*{Acknowledgments}} 801 | 802 | \newcommand{\keywords}{\paragraph*{Keywords}} 803 | 804 | \newcommand{\terms}{\paragraph*{General Terms}} 805 | 806 | % Identification 807 | % -------------- 808 | 809 | 810 | \def \@conferencename {} 811 | \def \@conferenceinfo {} 812 | \def \@copyrightyear {} 813 | \def \@copyrightdata {[to be supplied]} 814 | \def \@proceedings {[Unknown Proceedings]} 815 | 816 | 817 | \newcommand{\conferenceinfo}[2]{% 818 | \gdef \@conferencename {#1}% 819 | \gdef \@conferenceinfo {#2}} 820 | 821 | \newcommand{\copyrightyear}[1]{% 822 | \gdef \@copyrightyear {#1}} 823 | 824 | \let \CopyrightYear = \copyrightyear 825 | 826 | \newcommand{\copyrightdata}[1]{% 827 | \gdef \@copyrightdata {#1}} 828 | 829 | \let \crdata = \copyrightdata 830 | 831 | \newcommand{\proceedings}[1]{% 832 | \gdef \@proceedings {#1}} 833 | 834 | % Lists 835 | % ----- 836 | 837 | 838 | \setlength{\leftmargini}{13pt} 839 | \setlength\leftmarginii{13pt} 840 | \setlength\leftmarginiii{13pt} 841 | \setlength\leftmarginiv{13pt} 842 | \setlength{\labelsep}{3.5pt} 843 | 844 | \setlength{\topsep}{\standardvspace} 845 | \if \@blockstyle 846 | \setlength{\itemsep}{1pt} 847 | \setlength{\parsep}{3pt} 848 | \else 849 | \setlength{\itemsep}{1pt} 850 | \setlength{\parsep}{3pt} 851 | \fi 852 | 853 | \renewcommand{\labelitemi}{{\small \centeroncapheight{\textbullet}}} 854 | \renewcommand{\labelitemii}{\centeroncapheight{\rule{2.5pt}{2.5pt}}} 855 | \renewcommand{\labelitemiii}{$-$} 856 | \renewcommand{\labelitemiv}{{\Large \textperiodcentered}} 857 | 858 | \renewcommand{\@listi}{% 859 | \leftmargin = \leftmargini 860 | \listparindent = 0pt} 861 | %%% \itemsep = 1pt 862 | %%% \parsep = 3pt} 863 | %%% \listparindent = \parindent} 864 | 865 | \let \@listI = \@listi 866 | 867 | \renewcommand{\@listii}{% 868 | \leftmargin = \leftmarginii 869 | \topsep = 1pt 870 | \labelwidth = \leftmarginii 871 | \advance \labelwidth by -\labelsep 872 | \listparindent = \parindent} 873 | 874 | \renewcommand{\@listiii}{% 875 | \leftmargin = \leftmarginiii 876 | \labelwidth = \leftmarginiii 877 | \advance \labelwidth by -\labelsep 878 | \listparindent = \parindent} 879 | 880 | \renewcommand{\@listiv}{% 881 | \leftmargin = \leftmarginiv 882 | \labelwidth = \leftmarginiv 883 | \advance \labelwidth by -\labelsep 884 | \listparindent = \parindent} 885 | 886 | % Mathematics 887 | % ----------- 888 | 889 | 890 | \def \theequation {\arabic{equation}} 891 | 892 | % Miscellaneous 893 | % ------------- 894 | 895 | 896 | \newcommand{\balancecolumns}{% 897 | \vfill\eject 898 | \global\@colht = \textheight 899 | \global\ht\@cclv = \textheight} 900 | 901 | \newcommand{\nut}{\hspace{.5em}} 902 | 903 | \newcommand{\softraggedright}{% 904 | \let \\ = \@centercr 905 | \leftskip = 0pt 906 | \rightskip = 0pt plus 10pt} 907 | 908 | % Program Code 909 | % ------- ---- 910 | 911 | 912 | \newcommand{\mono}[1]{% 913 | {\@tempdima = \fontdimen2\font 914 | \texttt{\spaceskip = 1.1\@tempdima #1}}} 915 | 916 | % Running Heads and Feet 917 | % ------- ----- --- ---- 918 | 919 | 920 | \def \@preprintfooter {} 921 | 922 | \newcommand{\preprintfooter}[1]{% 923 | \gdef \@preprintfooter {#1}} 924 | 925 | \if \@preprint 926 | 927 | \def \ps@plain {% 928 | \let \@mkboth = \@gobbletwo 929 | \let \@evenhead = \@empty 930 | \def \@evenfoot {\scriptsize 931 | \rlap{\textit{\@preprintfooter}}\hfil 932 | \thepage \hfil 933 | \llap{\textit{\@formatyear}}}% 934 | \let \@oddhead = \@empty 935 | \let \@oddfoot = \@evenfoot} 936 | 937 | \else\if \@reprint 938 | 939 | \def \ps@plain {% 940 | \let \@mkboth = \@gobbletwo 941 | \let \@evenhead = \@empty 942 | \def \@evenfoot {\scriptsize \hfil \thepage \hfil}% 943 | \let \@oddhead = \@empty 944 | \let \@oddfoot = \@evenfoot} 945 | 946 | \else 947 | 948 | \let \ps@plain = \ps@empty 949 | \let \ps@headings = \ps@empty 950 | \let \ps@myheadings = \ps@empty 951 | 952 | \fi\fi 953 | 954 | \def \@formatyear {% 955 | \number\year/\number\month/\number\day} 956 | 957 | % Special Characters 958 | % ------- ---------- 959 | 960 | 961 | \DeclareRobustCommand{\euro}{% 962 | \protect{\rlap{=}}{\sf \kern .1em C}} 963 | 964 | % Title Page 965 | % ----- ---- 966 | 967 | 968 | \@setflag \@addauthorsdone = \@false 969 | 970 | \def \@titletext {\@latex@error{No title was provided}{}} 971 | \def \@subtitletext {} 972 | 973 | \newcount{\@authorcount} 974 | 975 | \newcount{\@titlenotecount} 976 | \newtoks{\@titlenotetext} 977 | 978 | \def \@titlebanner {} 979 | 980 | \renewcommand{\title}[1]{% 981 | \gdef \@titletext {#1}} 982 | 983 | \newcommand{\subtitle}[1]{% 984 | \gdef \@subtitletext {#1}} 985 | 986 | \newcommand{\authorinfo}[3]{% {names}{affiliation}{email/URL} 987 | \global\@increment \@authorcount 988 | \@withname\gdef {\@authorname\romannumeral\@authorcount}{#1}% 989 | \@withname\gdef {\@authoraffil\romannumeral\@authorcount}{#2}% 990 | \@withname\gdef {\@authoremail\romannumeral\@authorcount}{#3}} 991 | 992 | \renewcommand{\author}[1]{% 993 | \@latex@error{The \string\author\space command is obsolete; 994 | use \string\authorinfo}{}} 995 | 996 | \newcommand{\titlebanner}[1]{% 997 | \gdef \@titlebanner {#1}} 998 | 999 | \renewcommand{\maketitle}{% 1000 | \pagestyle{plain}% 1001 | \if \@onecolumn 1002 | {\hsize = \standardtextwidth 1003 | \@maketitle}% 1004 | \else 1005 | \twocolumn[\@maketitle]% 1006 | \fi 1007 | \@placetitlenotes 1008 | \if \@copyrightwanted \@copyrightspace \fi} 1009 | 1010 | \def \@maketitle {% 1011 | \begin{center} 1012 | \@settitlebanner 1013 | \let \thanks = \titlenote 1014 | {\leftskip = 0pt plus 0.25\linewidth 1015 | \rightskip = 0pt plus 0.25 \linewidth 1016 | \parfillskip = 0pt 1017 | \spaceskip = .7em 1018 | \noindent \LARGE \bfseries \@titletext \par} 1019 | \vskip 6pt 1020 | \noindent \Large \@subtitletext \par 1021 | \vskip 12pt 1022 | \ifcase \@authorcount 1023 | \@latex@error{No authors were specified for this paper}{}\or 1024 | \@titleauthors{i}{}{}\or 1025 | \@titleauthors{i}{ii}{}\or 1026 | \@titleauthors{i}{ii}{iii}\or 1027 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{}{}\or 1028 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{}\or 1029 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{vi}\or 1030 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{vi}% 1031 | \@titleauthors{vii}{}{}\or 1032 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{vi}% 1033 | \@titleauthors{vii}{viii}{}\or 1034 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{vi}% 1035 | \@titleauthors{vii}{viii}{ix}\or 1036 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{vi}% 1037 | \@titleauthors{vii}{viii}{ix}\@titleauthors{x}{}{}\or 1038 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{vi}% 1039 | \@titleauthors{vii}{viii}{ix}\@titleauthors{x}{xi}{}\or 1040 | \@titleauthors{i}{ii}{iii}\@titleauthors{iv}{v}{vi}% 1041 | \@titleauthors{vii}{viii}{ix}\@titleauthors{x}{xi}{xii}% 1042 | \else 1043 | \@latex@error{Cannot handle more than 12 authors}{}% 1044 | \fi 1045 | \vspace{1.75pc} 1046 | \end{center}} 1047 | 1048 | \def \@settitlebanner {% 1049 | \if \@andp{\@preprint}{\@notp{\@emptydefp{\@titlebanner}}}% 1050 | \vbox to 0pt{% 1051 | \vskip -32pt 1052 | \noindent \textbf{\@titlebanner}\par 1053 | \vss}% 1054 | \nointerlineskip 1055 | \fi} 1056 | 1057 | \def \@titleauthors #1#2#3{% 1058 | \if \@andp{\@emptyargp{#2}}{\@emptyargp{#3}}% 1059 | \noindent \@setauthor{40pc}{#1}{\@false}\par 1060 | \else\if \@emptyargp{#3}% 1061 | \noindent \@setauthor{17pc}{#1}{\@false}\hspace{3pc}% 1062 | \@setauthor{17pc}{#2}{\@false}\par 1063 | \else 1064 | \noindent \@setauthor{12.5pc}{#1}{\@false}\hspace{2pc}% 1065 | \@setauthor{12.5pc}{#2}{\@false}\hspace{2pc}% 1066 | \@setauthor{12.5pc}{#3}{\@true}\par 1067 | \relax 1068 | \fi\fi 1069 | \vspace{20pt}} 1070 | 1071 | \def \@setauthor #1#2#3{% {width}{text}{unused} 1072 | \vtop{% 1073 | \def \and {% 1074 | \hspace{16pt}} 1075 | \hsize = #1 1076 | \normalfont 1077 | \centering 1078 | \large \@name{\@authorname#2}\par 1079 | \vspace{5pt} 1080 | \normalsize \@name{\@authoraffil#2}\par 1081 | \vspace{2pt} 1082 | \textsf{\@name{\@authoremail#2}}\par}} 1083 | 1084 | \def \@maybetitlenote #1{% 1085 | \if \@andp{#1}{\@gtrp{\@authorcount}{3}}% 1086 | \titlenote{See page~\pageref{@addauthors} for additional authors.}% 1087 | \fi} 1088 | 1089 | \newtoks{\@fnmark} 1090 | 1091 | \newcommand{\titlenote}[1]{% 1092 | \global\@increment \@titlenotecount 1093 | \ifcase \@titlenotecount \relax \or 1094 | \@fnmark = {\ast}\or 1095 | \@fnmark = {\dagger}\or 1096 | \@fnmark = {\ddagger}\or 1097 | \@fnmark = {\S}\or 1098 | \@fnmark = {\P}\or 1099 | \@fnmark = {\ast\ast}% 1100 | \fi 1101 | \,$^{\the\@fnmark}$% 1102 | \edef \reserved@a {\noexpand\@appendtotext{% 1103 | \noexpand\@titlefootnote{\the\@fnmark}}}% 1104 | \reserved@a{#1}} 1105 | 1106 | \def \@appendtotext #1#2{% 1107 | \global\@titlenotetext = \expandafter{\the\@titlenotetext #1{#2}}} 1108 | 1109 | \newcount{\@authori} 1110 | 1111 | \iffalse 1112 | \def \additionalauthors {% 1113 | \if \@gtrp{\@authorcount}{3}% 1114 | \section{Additional Authors}% 1115 | \label{@addauthors}% 1116 | \noindent 1117 | \@authori = 4 1118 | {\let \\ = ,% 1119 | \loop 1120 | \textbf{\@name{\@authorname\romannumeral\@authori}}, 1121 | \@name{\@authoraffil\romannumeral\@authori}, 1122 | email: \@name{\@authoremail\romannumeral\@authori}.% 1123 | \@increment \@authori 1124 | \if \@notp{\@gtrp{\@authori}{\@authorcount}} \repeat}% 1125 | \par 1126 | \fi 1127 | \global\@setflag \@addauthorsdone = \@true} 1128 | \fi 1129 | 1130 | \let \addauthorsection = \additionalauthors 1131 | 1132 | \def \@placetitlenotes { 1133 | \the\@titlenotetext} 1134 | 1135 | % Utilities 1136 | % --------- 1137 | 1138 | 1139 | \newcommand{\centeroncapheight}[1]{% 1140 | {\setbox\@tempboxa = \hbox{#1}% 1141 | \@measurecapheight{\@tempdima}% % Calculate ht(CAP) - ht(text) 1142 | \advance \@tempdima by -\ht\@tempboxa % ------------------ 1143 | \divide \@tempdima by 2 % 2 1144 | \raise \@tempdima \box\@tempboxa}} 1145 | 1146 | \newbox{\@measbox} 1147 | 1148 | \def \@measurecapheight #1{% {\dimen} 1149 | \setbox\@measbox = \hbox{ABCDEFGHIJKLMNOPQRSTUVWXYZ}% 1150 | #1 = \ht\@measbox} 1151 | 1152 | \long\def \@titlefootnote #1#2{% 1153 | \insert\footins{% 1154 | \reset@font\footnotesize 1155 | \interlinepenalty\interfootnotelinepenalty 1156 | \splittopskip\footnotesep 1157 | \splitmaxdepth \dp\strutbox \floatingpenalty \@MM 1158 | \hsize\columnwidth \@parboxrestore 1159 | %%% \protected@edef\@currentlabel{% 1160 | %%% \csname p@footnote\endcsname\@thefnmark}% 1161 | \color@begingroup 1162 | \def \@makefnmark {$^{#1}$}% 1163 | \@makefntext{% 1164 | \rule\z@\footnotesep\ignorespaces#2\@finalstrut\strutbox}% 1165 | \color@endgroup}} 1166 | 1167 | % LaTeX Modifications 1168 | % ----- ------------- 1169 | 1170 | \def \@seccntformat #1{% 1171 | \@name{\the#1}% 1172 | \@expandaftertwice\@seccntformata \csname the#1\endcsname.\@mark 1173 | \quad} 1174 | 1175 | \def \@seccntformata #1.#2\@mark{% 1176 | \if \@emptyargp{#2}.\fi} 1177 | 1178 | % Revision History 1179 | % -------- ------- 1180 | 1181 | 1182 | % Date Person Ver. Change 1183 | % ---- ------ ---- ------ 1184 | 1185 | % 2004.09.12 PCA 0.1--5 Preliminary development. 1186 | 1187 | % 2004.11.18 PCA 0.5 Start beta testing. 1188 | 1189 | % 2004.11.19 PCA 0.6 Obsolete \author and replace with 1190 | % \authorinfo. 1191 | % Add 'nocopyrightspace' option. 1192 | % Compress article opener spacing. 1193 | % Add 'mathtime' option. 1194 | % Increase text height by 6 points. 1195 | 1196 | % 2004.11.28 PCA 0.7 Add 'cm/computermodern' options. 1197 | % Change default to Times text. 1198 | 1199 | % 2004.12.14 PCA 0.8 Remove use of mathptm.sty; it cannot 1200 | % coexist with latexsym or amssymb. 1201 | 1202 | % 2005.01.20 PCA 0.9 Rename class file to sigplanconf.cls. 1203 | 1204 | % 2005.03.05 PCA 0.91 Change default copyright data. 1205 | 1206 | % 2005.03.06 PCA 0.92 Add at-signs to some macro names. 1207 | 1208 | % 2005.03.07 PCA 0.93 The 'onecolumn' option defaults to '11pt', 1209 | % and it uses the full type width. 1210 | 1211 | % 2005.03.15 PCA 0.94 Add at-signs to more macro names. 1212 | % Allow margin paragraphs during review. 1213 | 1214 | % 2005.03.22 PCA 0.95 Implement \euro. 1215 | % Remove proof and newdef environments. 1216 | 1217 | % 2005.05.06 PCA 1.0 Eliminate 'onecolumn' option. 1218 | % Change footer to small italic and eliminate 1219 | % left portion if no \preprintfooter. 1220 | % Eliminate copyright notice if preprint. 1221 | % Clean up and shrink copyright box. 1222 | 1223 | % 2005.05.30 PCA 1.1 Add alternate permission statements. 1224 | 1225 | % 2005.06.29 PCA 1.1 Publish final first edition of guide. 1226 | 1227 | % 2005.07.14 PCA 1.2 Add \subparagraph. 1228 | % Use block paragraphs in lists, and adjust 1229 | % spacing between items and paragraphs. 1230 | 1231 | % 2006.06.22 PCA 1.3 Add 'reprint' option and associated 1232 | % commands. 1233 | 1234 | % 2006.08.24 PCA 1.4 Fix bug in \maketitle case command. 1235 | 1236 | % 2007.03.13 PCA 1.5 The title banner only displays with the 1237 | % 'preprint' option. 1238 | 1239 | % 2007.06.06 PCA 1.6 Use \bibfont in \thebibliography. 1240 | % Add 'natbib' option to load and configure 1241 | % the natbib package. 1242 | 1243 | % 2007.11.20 PCA 1.7 Balance line lengths in centered article 1244 | % title (thanks to Norman Ramsey). 1245 | 1246 | % 2009.01.26 PCA 1.8 Change natbib \bibpunct values. 1247 | 1248 | % 2009.03.24 PCA 1.9 Change natbib to use the 'numbers' option. 1249 | % Change templates to use 'natbib' option. 1250 | 1251 | % 2009.09.01 PCA 2.0 Add \reprintprice command (suggested by 1252 | % Stephen Chong). 1253 | 1254 | % 2009.09.08 PCA 2.1 Make 'natbib' the default; add 'nonatbib'. 1255 | % SB Add 'authoryear' and 'numbers' (default) to 1256 | % control citation style when using natbib. 1257 | % Add \bibpunct to change punctuation for 1258 | % 'authoryear' style. 1259 | 1260 | % 2009.09.21 PCA 2.2 Add \softraggedright to the thebibliography 1261 | % environment. Also add to template so it will 1262 | % happen with natbib. 1263 | 1264 | % 2009.09.30 PCA 2.3 Remove \softraggedright from thebibliography. 1265 | % Just include in the template. 1266 | 1267 | % 2010.05.24 PCA 2.4 Obfuscate author's email address. 1268 | 1269 | % 2011.11.08 PCA 2.5 Add copyright notice to this file. 1270 | % Remove 'sort' option from natbib when using 1271 | % 'authoryear' style. 1272 | % Add the \authorversion command. 1273 | -------------------------------------------------------------------------------- /doc/textgreek.sty: -------------------------------------------------------------------------------- 1 | %% 2 | %% This is file `textgreek.sty', 3 | %% generated with the docstrip utility. 4 | %% 5 | %% The original source files were: 6 | %% 7 | %% textgreek.dtx (with options: `package') 8 | %% 9 | %% This is a generated file. 10 | %% 11 | %% Copyright 2010,2011 Leonard Michlmayr 12 | %% 13 | %% This work may be distributed and/or modified under the 14 | %% conditions of the LaTeX Project Public License, either version 1.3 15 | %% of this license or (at your option) any later version. 16 | %% The latest version of this license is in 17 | %% http://www.latex-project.org/lppl.txt 18 | %% and version 1.3 or later is part of all distributions of LaTeX 19 | %% version 2005/12/01 or later. 20 | %% 21 | %% This work has the LPPL maintenance status `author-maintained'. 22 | %% 23 | %% The Current Maintainer of this work is Leonard Michlmayr 24 | %% . 25 | %% 26 | %% This work consists of the file textgreek.dtx 27 | %% and the derived files textgreek.sty and textgreek.pdf 28 | %% 29 | \NeedsTeXFormat{LaTeX2e}[2009/09/24] 30 | \ProvidesPackage{textgreek} 31 | [2011/04/08 v0.5 greek symbols in text] 32 | \InputIfFileExists{lgrenc.def}{% 33 | \PackageInfo{textgreek}{Loading the definitions for the Greek font% 34 | encoding.}}{% 35 | \PackageError{textgreek}{Cannot find the file lgrenc.def}{% 36 | lgrenc.def is a file that contains the definitions for the Greek 37 | font encoding LGR. Maybe it comes with the babel package.}} 38 | \DeclareOption{cbgreek}{% 39 | \renewcommand*{\textgreekfontmap}{% 40 | {eur/*/*}{U/eur/*/*} 41 | {phv/*/*}{U/psy/*/*}}}% 42 | \DeclareOption{euler}{% 43 | \renewcommand*{\textgreekfontmap}{% 44 | {phv/*/*}{U/psy/*/*} 45 | {*/bx/n}{U/eur/b/n} 46 | {*/b/n}{U/eur/b/n} 47 | {*/*/n}{U/eur/m/n}}}% 48 | \DeclareOption{artemisia}{% 49 | \renewcommand*{\textgreekfontmap}{% 50 | {eur/*/*}{U/eur/*/*} 51 | {phv/*/*}{U/psy/*/*} 52 | {*/b/n}{LGR/artemisia/b/n} 53 | {*/bx/n}{LGR/artemisia/bx/n} 54 | {*/*/n}{LGR/artemisia/m/n} 55 | {*/b/it}{LGR/artemisia/b/it} 56 | {*/bx/it}{LGR/artemisia/bx/it} 57 | {*/*/it}{LGR/artemisia/m/it} 58 | {*/b/sl}{LGR/artemisia/b/sl} 59 | {*/bx/sl}{LGR/artemisia/bx/sl} 60 | {*/*/sl}{LGR/artemisia/m/sl} 61 | {*/*/sc}{LGR/artemisia/m/sc} 62 | {*/*/sco}{LGR/artemisia/m/sco}}}% 63 | \newcommand*{\textgreekfontmap}{}% 64 | \ExecuteOptions{cbgreek} 65 | \ProcessOptions\relax% 66 | \PackageInfo{textgreek}{Loaded fontmap: \textgreekfontmap.} 67 | \def\textgreek@setfont#1/#2/#3/#4\relax{% 68 | \textgreek@ematch{#1}{*}{}{\fontencoding{#1}}% 69 | \textgreek@ematch{#2}{*}{}{\fontfamily{#2}}% 70 | \textgreek@ematch{#3}{*}{}{\fontseries{#3}}% 71 | \textgreek@ematch{#4}{*}{}{\fontshape{#4}}}% 72 | \def\textgreek@eof{}% 73 | \def\textgreek@return#1#2\textgreek@eof{% 74 | \fi#1}% 75 | \def\textgreek@ematch#1#2#3#4{% 76 | \begingroup% 77 | \edef\tempa{#1}\edef\tempb{#2}\def\tempc{*}% 78 | \def\return##1##2\endgroup{\fi\endgroup##1}% 79 | \ifx\tempa\tempb\return{#3}\fi% 80 | \ifx\tempa\tempc\return{#3}\fi% 81 | \iftrue\return{#4}\fi% 82 | \endgroup}% 83 | \def\textgreek@matchfont#1/#2/#3\relax#4#5{% 84 | \textgreek@ematch{#1}{\f@family}{% 85 | \textgreek@ematch{#2}{\f@series}{% 86 | \textgreek@ematch{#3}{\f@shape}{#4}{#5}}% 87 | {#5}}% 88 | {#5}% 89 | }% 90 | \def\textgreek@findfont@#1#2#3\textgreek@eof{% 91 | \textgreek@matchfont#1\relax% 92 | {\textgreek@setfont#2\relax}% 93 | {\textgreek@findfont#3\textgreek@eof}}% 94 | \def\textgreek@findfont#1\textgreek@eof{% 95 | \begingroup% 96 | \def\temp{#1}% 97 | \def\return##1##2\endgroup{\fi\endgroup##1}% 98 | \ifx\temp\textgreek@eof\else% 99 | \return{\textgreek@findfont@#1\textgreek@eof}% 100 | \fi\endgroup}% 101 | \newcommand*{\textgreekfont}{% 102 | \fontencoding{LGR}% 103 | \edef\textgreek@fontmap{\textgreekfontmap}% 104 | \expandafter\textgreek@findfont\textgreek@fontmap\textgreek@eof% 105 | \selectfont% 106 | }% 107 | \newcommand*{\lgrtoeuler}[1]{% 108 | \if G#1\textgreek@return{\char0}\fi% 109 | \if D#1\textgreek@return{\char1}\fi% 110 | \if J#1\textgreek@return{\char2}\fi% 111 | \if L#1\textgreek@return{\char3}\fi% 112 | \if X#1\textgreek@return{\char4}\fi% 113 | \if P#1\textgreek@return{\char5}\fi% 114 | \if S#1\textgreek@return{\char6}\fi% 115 | \if U#1\textgreek@return{\char7}\fi% 116 | \if F#1\textgreek@return{\char8}\fi% 117 | \if Y#1\textgreek@return{\char9}\fi% 118 | \if W#1\textgreek@return{\char10}\fi% 119 | \if a#1\textgreek@return{\char11}\fi% 120 | \if b#1\textgreek@return{\char12}\fi% 121 | \if g#1\textgreek@return{\char13}\fi% 122 | \if d#1\textgreek@return{\char14}\fi% 123 | \if 3#1\textgreek@return{\char15}\fi% element-of style epsilon 124 | \if z#1\textgreek@return{\char16}\fi% 125 | \if h#1\textgreek@return{\char17}\fi% 126 | \if j#1\textgreek@return{\char18}\fi% temperature style theta 127 | \if i#1\textgreek@return{\char19}\fi% 128 | \if k#1\textgreek@return{\char20}\fi% 129 | \if l#1\textgreek@return{\char21}\fi% 130 | \if m#1\textgreek@return{\char22}\fi% 131 | \if n#1\textgreek@return{\char23}\fi% 132 | \if x#1\textgreek@return{\char24}\fi% 133 | \if p#1\textgreek@return{\char25}\fi% 134 | \if r#1\textgreek@return{\char26}\fi% 135 | \if s#1\textgreek@return{\char27}\fi% 136 | \if t#1\textgreek@return{\char28}\fi% 137 | \if u#1\textgreek@return{\char29}\fi% 138 | \if v#1\textgreek@return{\char30}\fi% o-slash style phi 139 | \if q#1\textgreek@return{\char31}\fi% 140 | \if y#1\textgreek@return{\char32}\fi% 141 | \if w#1\textgreek@return{\char33}\fi% 142 | \if e#1\textgreek@return{\char34}\fi% 143 | \if 0#1\textgreek@return{\char35}\fi% 144 | \if f#1\textgreek@return{\char39}\fi% 145 | \fontencoding{LGR}\selectfont #1% 146 | \textgreek@eof}% 147 | \newcommand*{\lgrtosymbol}[1]{% 148 | \if A#1\textgreek@return{A}\fi% 149 | \if B#1\textgreek@return{B}\fi% 150 | \if Q#1\textgreek@return{C}\fi% 151 | \if D#1\textgreek@return{D}\fi% 152 | \if E#1\textgreek@return{E}\fi% 153 | \if F#1\textgreek@return{F}\fi% 154 | \if G#1\textgreek@return{G}\fi% 155 | \if H#1\textgreek@return{H}\fi% 156 | \if I#1\textgreek@return{I}\fi% 157 | \if 0#1\textgreek@return{J}\fi% 158 | \if K#1\textgreek@return{K}\fi% 159 | \if L#1\textgreek@return{L}\fi% 160 | \if M#1\textgreek@return{M}\fi% 161 | \if N#1\textgreek@return{N}\fi% 162 | \if O#1\textgreek@return{O}\fi% 163 | \if P#1\textgreek@return{P}\fi% 164 | \if J#1\textgreek@return{Q}\fi% 165 | \if R#1\textgreek@return{R}\fi% 166 | \if S#1\textgreek@return{S}\fi% 167 | \if T#1\textgreek@return{T}\fi% 168 | \if U#1\textgreek@return{U}\fi% 169 | \if c#1\textgreek@return{V}\fi% 170 | \if W#1\textgreek@return{W}\fi% 171 | \if X#1\textgreek@return{X}\fi% 172 | \if Y#1\textgreek@return{Y}\fi% 173 | \if Z#1\textgreek@return{Z}\fi% 174 | \if a#1\textgreek@return{a}\fi% 175 | \if b#1\textgreek@return{b}\fi% 176 | \if q#1\textgreek@return{c}\fi% 177 | \if d#1\textgreek@return{d}\fi% 178 | \if e#1\textgreek@return{e}\fi% 179 | \if v#1\textgreek@return{f}\fi% 180 | \if g#1\textgreek@return{g}\fi% 181 | \if h#1\textgreek@return{h}\fi% 182 | \if i#1\textgreek@return{i}\fi% 183 | \if f#1\textgreek@return{j}\fi% 184 | \if k#1\textgreek@return{k}\fi% 185 | \if l#1\textgreek@return{l}\fi% 186 | \if m#1\textgreek@return{m}\fi% 187 | \if n#1\textgreek@return{n}\fi% 188 | \if o#1\textgreek@return{o}\fi% 189 | \if p#1\textgreek@return{p}\fi% 190 | \if j#1\textgreek@return{q}\fi% 191 | \if r#1\textgreek@return{r}\fi% 192 | \if s#1\textgreek@return{s}\fi% 193 | \if t#1\textgreek@return{t}\fi% 194 | \if u#1\textgreek@return{u}\fi% 195 | \if w#1\textgreek@return{w}\fi% 196 | \if x#1\textgreek@return{x}\fi% 197 | \if y#1\textgreek@return{y}\fi% 198 | \if z#1\textgreek@return{z}\fi% 199 | \fontencoding{LGR}\selectfont #1% 200 | \textgreek@eof}% 201 | \DeclareRobustCommand*{\TextGreek}[1]{% 202 | \begingroup% 203 | \textgreekfont% 204 | \edef\tempa{\f@family}% 205 | \def\tempb{eur}\def\tempc{psy}% 206 | \ifx\tempa\tempb\textgreek@return{\lgrtoeuler#1}\fi% 207 | \ifx\tempa\tempc\textgreek@return{\lgrtosymbol#1}\fi% 208 | #1% 209 | \textgreek@eof% 210 | \endgroup}% 211 | \newcommand*{\DeclareTextGreekSymbol}[2]{% 212 | \expandafter\DeclareTextCommandDefault\csname text#1\endcsname% 213 | {\TextGreek{#2}}}% 214 | \DeclareTextGreekSymbol{alpha}{a} 215 | \DeclareTextGreekSymbol{beta}{b} 216 | \DeclareTextGreekSymbol{gamma}{g} 217 | \DeclareTextGreekSymbol{delta}{d} 218 | \DeclareTextGreekSymbol{epsilon}{e} 219 | \DeclareTextGreekSymbol{zeta}{z} 220 | \DeclareTextGreekSymbol{eta}{h} 221 | \DeclareTextGreekSymbol{theta}{j} 222 | \DeclareTextGreekSymbol{iota}{i} 223 | \DeclareTextGreekSymbol{kappa}{k} 224 | \DeclareTextGreekSymbol{lambda}{l} 225 | \expandafter\ifx\csname?\string\textmu\endcsname\relax% 226 | \DeclareTextGreekSymbol{mu}{m}\fi 227 | \DeclareTextGreekSymbol{mugreek}{m} 228 | \DeclareTextGreekSymbol{nu}{n} 229 | \DeclareTextGreekSymbol{xi}{x} 230 | \DeclareTextGreekSymbol{omikron}{o} 231 | \DeclareTextGreekSymbol{pi}{p} 232 | \DeclareTextGreekSymbol{rho}{r} 233 | \DeclareTextGreekSymbol{sigma}{s\noboundary} 234 | \DeclareTextGreekSymbol{varsigma}{c} 235 | \DeclareTextGreekSymbol{tau}{t} 236 | \DeclareTextGreekSymbol{upsilon}{u} 237 | \DeclareTextGreekSymbol{phi}{f} 238 | \DeclareTextGreekSymbol{chi}{q} 239 | \DeclareTextGreekSymbol{psi}{y} 240 | \DeclareTextGreekSymbol{omega}{w} 241 | \DeclareTextGreekSymbol{Alpha}{A} 242 | \DeclareTextGreekSymbol{Beta}{B} 243 | \DeclareTextGreekSymbol{Gamma}{G} 244 | \DeclareTextGreekSymbol{Delta}{D} 245 | \DeclareTextGreekSymbol{Epsilon}{E} 246 | \DeclareTextGreekSymbol{Zeta}{Z} 247 | \DeclareTextGreekSymbol{Eta}{H} 248 | \DeclareTextGreekSymbol{Theta}{J} 249 | \DeclareTextGreekSymbol{Iota}{I} 250 | \DeclareTextGreekSymbol{Kappa}{K} 251 | \DeclareTextGreekSymbol{Lambda}{L} 252 | \DeclareTextGreekSymbol{Mu}{M} 253 | \DeclareTextGreekSymbol{Nu}{N} 254 | \DeclareTextGreekSymbol{Xi}{X} 255 | \DeclareTextGreekSymbol{Omikron}{O} 256 | \DeclareTextGreekSymbol{Pi}{P} 257 | \DeclareTextGreekSymbol{Rho}{R} 258 | \DeclareTextGreekSymbol{Sigma}{S} 259 | \DeclareTextGreekSymbol{Tau}{T} 260 | \DeclareTextGreekSymbol{Upsilon}{U} 261 | \DeclareTextGreekSymbol{Phi}{F} 262 | \DeclareTextGreekSymbol{Chi}{Q} 263 | \DeclareTextGreekSymbol{Psi}{Y} 264 | \DeclareTextGreekSymbol{Omega}{W} 265 | \DeclareTextCommand{\straightphi}{PU}% 266 | {\83\325} % U+03D5 GREEK PHI SYMBOL 267 | \DeclareTextCommandDefault{\straightphi}{% 268 | \begingroup\textgreekfont% 269 | \edef\tempa{\f@family}% 270 | \edef\tempb{\f@shape}% 271 | \def\tempc{eur}\def\tempd{psy}% 272 | \def\tempe{it}% 273 | \ifx\tempa\tempc\textgreek@return{\lgrtoeuler{v}}\fi% 274 | \ifx\tempa\tempd\textgreek@return{\lgrtosymbol{v}}\fi% 275 | \ifx\tempb\tempe\textgreek@return{% 276 | \fontencoding{OML}\selectfont\char30}\fi% 277 | \fontencoding{U}\fontfamily{eur}\selectfont\lgrtoeuler{v}% 278 | \textgreek@eof% 279 | \endgroup}% 280 | \DeclareTextCommand{\scripttheta}{PU}% 281 | {\83\321}% U+03D1 GREEK THETA SYMBOL 282 | \DeclareTextCommandDefault{\scripttheta}{% 283 | \begingroup\textgreekfont% 284 | \edef\tempa{\f@family}% 285 | \edef\tempb{\f@shape}% 286 | \def\tempc{eur}\def\tempd{psy}% 287 | \def\tempe{it}% 288 | \ifx\tempa\tempc\textgreek@return{\lgrtoeuler{0}}\fi% 289 | \ifx\tempa\tempd\textgreek@return{\lgrtosymbol{0}}\fi% 290 | j% default theta in cbgreek and artemisia 291 | \textgreek@eof% 292 | \endgroup}% 293 | \DeclareTextCommand{\straighttheta}{PU}% 294 | {\83\270} % U+03B8 GREEK THETA SYMBOL 295 | \DeclareTextCommandDefault{\straighttheta}{% 296 | \begingroup\textgreekfont% 297 | \edef\tempa{\f@family}% 298 | \edef\tempb{\f@shape}% 299 | \def\tempc{eur}\def\tempd{psy}% 300 | \def\tempe{it}% 301 | \ifx\tempa\tempc\textgreek@return{\lgrtoeuler{j}}\fi% 302 | \ifx\tempa\tempd\textgreek@return{\lgrtosymbol{j}}\fi% 303 | \ifx\tempb\tempe\textgreek@return{% 304 | \fontencoding{OML}\selectfont\char18}\fi% 305 | \fontencoding{U}\fontfamily{eur}\selectfont\lgrtoeuler{j}% 306 | \textgreek@eof% 307 | \endgroup}% 308 | \iffalse\fi % U+03F5 GREEK LUNATE EPSILON SYMBOL 309 | \DeclareTextCommand{\straightepsilon}{PU}{\83\365}% 310 | \DeclareTextCommandDefault{\straightepsilon}{% 311 | \begingroup\textgreekfont% 312 | \edef\tempa{\f@family}% 313 | \edef\tempb{\f@shape}% 314 | \def\tempc{eur}\def\tempd{psy}% 315 | \def\tempe{it}% 316 | \ifx\tempa\tempc\textgreek@return{\lgrtoeuler{3}}\fi% 317 | \ifx\tempa\tempd\textgreek@return{% 318 | \fontfamily{eur}\fontseries{b}\selectfont\lgrtoeuler{3}}\fi% 319 | \ifx\tempb\tempe\textgreek@return{% 320 | \fontencoding{OML}\selectfont\char15}\fi% 321 | \fontencoding{U}\fontfamily{eur}\selectfont\lgrtoeuler{3}% 322 | \textgreek@eof% 323 | \endgroup}% 324 | \endinput 325 | %% 326 | %% End of file `textgreek.sty'. 327 | -------------------------------------------------------------------------------- /doc/todo.md: -------------------------------------------------------------------------------- 1 | [ ] Handle todos/comments in intro (make link to MPC) 2 | 3 | [ ] Decide on best solve algorithm (with/without intermediate data structure) 4 | 5 | [ ] Compress section on reflection (or keep it as is, but add a note 6 | that this is the part that people often leave out of papers). 7 | 8 | [ ] Add larger? harder? example. 9 | -------------------------------------------------------------------------------- /doc/typeclasses.lagda: -------------------------------------------------------------------------------- 1 | \section{Type classes} 2 | \label{sec:typeclasses} 3 | 4 | As a final application of our proof search algorithm, we show how it 5 | can be used to implement a \emph{type classes} in the style of 6 | Haskell. Souzeau and Oury~\cite{coq-type-classes} have already shown 7 | how to use Coq's proof search mechanism to construct 8 | dictionaries. Using Agda's \emph{instance 9 | arguments}~\cite{instance-args} and the proof search presented in 10 | this paper, we mimic their results. 11 | 12 | We begin by declaring our `type class' as a record containing the 13 | desired function: 14 | \begin{code} 15 | record Show (A : Set) : Set where 16 | field 17 | show : A → String 18 | \end{code} 19 | 20 | We can write instances for the |Show| `class' by constructing explicit 21 | dictionary objects: 22 | \begin{code} 23 | ShowBool : Show Bool 24 | ShowBool = record { show = ... } 25 | 26 | Showℕ : Show ℕ 27 | Showℕ = record { show = ... } 28 | \end{code} 29 | Using instance arguments, we can now call our |show| function without 30 | having to pass the required dictionary explicitly: 31 | \begin{code} 32 | open Show {{...}} 33 | 34 | example : String 35 | example = show 3 36 | \end{code} 37 | The instance argument mechanism infers that the |show| function is 38 | being called on a natural number, hence a dictionary of type |Show ℕ| 39 | is required. As there is only a single value of type |Show ℕ|, the 40 | required dictionary is inserted automatically. If we have multiple 41 | instance definitions for the same type or omit the required instance 42 | altogether, the Agda type checker would have given an error. 43 | 44 | It is more interesting to consider parametrized instances, such as 45 | the |Either| instance given below. 46 | \begin{code} 47 | ShowEither : Show A → Show B → Show (Either A B) 48 | ShowEither ShowA ShowB = record { show = showE } 49 | where 50 | showE : Either A B -> String 51 | showE (left x) = "left " ++ show x 52 | showE (right y) = "right " ++ show y 53 | \end{code} 54 | Unfortunately, instance arguments do not do any recursive search for 55 | suitable instances. Trying to call |show| on a value of type |Either ℕ 56 | Bool|, for example, will not succeed: the Agda type checker will 57 | complain that it cannot find a suitable instance argument. At the 58 | moment, the only way to resolve this is to construct the required 59 | instances manually: 60 | \begin{code} 61 | ShowEitherBoolℕ : Show (Either Bool ℕ) 62 | ShowEitherBoolℕ = ShowEither ShowBool Showℕ 63 | \end{code} 64 | Writing out such dictionaries is rather tedious. 65 | 66 | We can, however, use the |auto| function to construct the desired 67 | instance argument automatically. We start by putting the desired 68 | instances in a hint database: 69 | \begin{code} 70 | ShowHints : HintDB 71 | ShowHints = hintdb (quote ShowEither 72 | ∷ quote ShowBool 73 | ∷ quote Showℕ ∷ []) 74 | \end{code} 75 | 76 | The desired dictionary can now be assembled for us by calling the 77 | |auto| function: 78 | \begin{code} 79 | example : String 80 | example = show (left 4) ++ show (right true) 81 | where 82 | instance = tactic (auto 5 ShowHints) 83 | \end{code} 84 | Note that the type of the locally bound |instance| record is inferred 85 | in this example. Using this type, the |auto| function assembles the 86 | desired dictionary. When |show| is called on different types, however, 87 | we may still need to provide the type signatures of the instances we 88 | desire. 89 | While deceptively simple, this example illustrates how \emph{useful} 90 | it can be to have even a little automation. 91 | 92 | %%% Local Variables: 93 | %%% mode: latex 94 | %%% TeX-master: t 95 | %%% TeX-command-default: "rake" 96 | %%% End: 97 | -------------------------------------------------------------------------------- /src/Auto.agda: -------------------------------------------------------------------------------- 1 | open import Function using (const; id) 2 | open import Auto.Core using (IsHintDB; simpleHintDB; Rules; Rule; name2rule) 3 | open import Data.List using ([]; [_]; _++_) 4 | open import Data.Nat using (ℕ) 5 | open import Data.Product using (_,_) 6 | open import Data.Sum using (inj₁; inj₂) 7 | open import Reflection using (Name; Term; TC) 8 | 9 | module Auto where 10 | 11 | open import Auto.Extensible simpleHintDB public renaming (auto to auto′) 12 | 13 | auto : ℕ → HintDB → Term → TC Term 14 | auto = auto′ dfs 15 | 16 | macro 17 | autoMacro = auto 18 | -------------------------------------------------------------------------------- /src/Auto/Core.agda: -------------------------------------------------------------------------------- 1 | open import Level using (Level) 2 | open import Function using (_∘_; id; flip) 3 | open import Data.Fin as Fin using (fromℕ) 4 | open import Data.Nat as Nat using (ℕ; suc; zero; pred; _+_; _⊔_) 5 | open import Data.Nat.Properties using (≤-decTotalOrder) 6 | open import Data.List as List using (List; []; _∷_; [_]; concatMap; _++_; length; map) 7 | open import Data.Vec as Vec using (Vec; []; _∷_; _∷ʳ_; reverse; initLast; toList) 8 | open import Data.Product as Prod using (∃; _×_; _,_; proj₁; proj₂) 9 | open import Data.Maybe as Maybe using (Maybe; just; nothing; maybe) 10 | open import Data.Sum as Sum using (_⊎_; inj₁; inj₂) 11 | open import Data.Integer as Int using (ℤ; -[1+_]; +_) renaming (_≟_ to _≟-Int_) 12 | open import Relation.Nullary using (Dec; yes; no) 13 | open import Relation.Nullary.Decidable using (map′) 14 | open import Relation.Binary using (module DecTotalOrder) 15 | open import Relation.Binary.PropositionalEquality using (_≡_; refl; cong; sym) 16 | open import Reflection renaming (Term to AgTerm; _≟_ to _≟-AgTerm_) 17 | 18 | module Auto.Core where 19 | 20 | open DecTotalOrder ≤-decTotalOrder using (total) 21 | 22 | private 23 | ∃-syntax : ∀ {a b} {A : Set a} → (A → Set b) → Set (b Level.⊔ a) 24 | ∃-syntax = ∃ 25 | syntax ∃-syntax (λ x → B) = ∃[ x ] B 26 | 27 | 28 | -- define error messages that may occur when the `auto` function is 29 | -- called. 30 | data Message : Set where 31 | searchSpaceExhausted : Message 32 | unsupportedSyntax : Message 33 | 34 | 35 | -- define our own instance of the error functor based on the either 36 | -- monad, and use it to propagate one of several error messages 37 | private 38 | Error : ∀ {a} (A : Set a) → Set a 39 | Error A = Message ⊎ A 40 | 41 | _⟨$⟩_ : ∀ {a b} {A : Set a} {B : Set b} (f : A → B) → Error A → Error B 42 | f ⟨$⟩ inj₁ x = inj₁ x 43 | f ⟨$⟩ inj₂ y = inj₂ (f y) 44 | 45 | 46 | -- define term names for the term language we'll be using for proof 47 | -- search; we use standard Agda names, together with term-variables 48 | -- and Agda implications/function types. 49 | data TermName : Set₀ where 50 | name : (n : Name) → TermName 51 | var : (i : ℤ) → TermName 52 | impl : TermName 53 | 54 | tname-injective : ∀ {x y} → TermName.name x ≡ TermName.name y → x ≡ y 55 | tname-injective refl = refl 56 | 57 | tvar-injective : ∀ {i j} → TermName.var i ≡ TermName.var j → i ≡ j 58 | tvar-injective refl = refl 59 | 60 | _≟-TermName_ : (x y : TermName) → Dec (x ≡ y) 61 | (name x) ≟-TermName (name y) with x ≟-Name y 62 | (name x) ≟-TermName (name .x) | yes refl = yes refl 63 | (name x) ≟-TermName (name y) | no x≠y = no (x≠y ∘ tname-injective) 64 | (name _) ≟-TermName (var _) = no (λ ()) 65 | (name _) ≟-TermName (impl ) = no (λ ()) 66 | (var _) ≟-TermName (name _) = no (λ ()) 67 | (var i) ≟-TermName (var j) with i ≟-Int j 68 | (var i) ≟-TermName (var .i) | yes refl = yes refl 69 | (var i) ≟-TermName (var j) | no i≠j = no (i≠j ∘ tvar-injective) 70 | (var _) ≟-TermName (impl ) = no (λ ()) 71 | (impl ) ≟-TermName (name _) = no (λ ()) 72 | (impl ) ≟-TermName (var _) = no (λ ()) 73 | (impl ) ≟-TermName (impl ) = yes refl 74 | 75 | -- define rule names for the proof terms/rules that our proof search will 76 | -- return/use; we'll use standard Agda names, together with rule-variables. 77 | data RuleName : Set where 78 | name : Name → RuleName 79 | var : ℕ → RuleName 80 | 81 | name-injective : ∀ {x y} → RuleName.name x ≡ name y → x ≡ y 82 | name-injective refl = refl 83 | 84 | rvar-injective : ∀ {x y} → RuleName.var x ≡ var y → x ≡ y 85 | rvar-injective refl = refl 86 | 87 | _≟-RuleName_ : (x y : RuleName) → Dec (x ≡ y) 88 | name x ≟-RuleName name y = map′ (cong name) name-injective (x ≟-Name y) 89 | name x ≟-RuleName var y = no (λ ()) 90 | var x ≟-RuleName name y = no (λ ()) 91 | var x ≟-RuleName var y = map′ (cong var) rvar-injective (x Nat.≟ y) 92 | 93 | -- now we can load the definitions from proof search 94 | open import ProofSearch RuleName TermName _≟-TermName_ Literal _≟-Lit_ 95 | as PS public renaming (Term to PsTerm; module Extensible to PsExtensible) 96 | 97 | -- next up, converting the terms returned by Agda's reflection 98 | -- mechanism to terms in our proof search's language! 99 | 100 | 101 | -- dictionary for the treatment of variables in conversion from Agda 102 | -- terms to terms to be used in proof search. 103 | ConvertVar : Set 104 | ConvertVar = (depth index : ℕ) → ∃ PsTerm 105 | 106 | -- conversion dictionary for rule-terms, which turns every variable 107 | -- that is within the scope of the term (i.e. is defined within the 108 | -- term by lambda abstraction) into a variable, and every variable 109 | -- which is defined out of scope into a Skolem constant (which 110 | -- blocks unification). 111 | convertVar4Term : ConvertVar 112 | convertVar4Term = fromVar 113 | where 114 | fromVar : (depth index : ℕ) → ∃ PsTerm 115 | fromVar d i with total i d 116 | fromVar d i | inj₁ i≤d = (suc (Δ i≤d) , var (fromℕ (Δ i≤d))) 117 | fromVar d i | inj₂ i>d = (0 , con (var (-[1+ Δ i>d ])) []) 118 | 119 | -- conversion dictionary for goal-terms, which turns all variables 120 | -- into Skolem constants which blocks all unification. 121 | convertVar4Goal : ConvertVar 122 | convertVar4Goal = fromVar 123 | where 124 | fromVar : (depth index : ℕ) → ∃ PsTerm 125 | fromVar d i with total i d 126 | fromVar d i | inj₁ i≤d = (0 , con (var (+ Δ i≤d)) []) 127 | fromVar d i | inj₂ i>d = (0 , con (var (-[1+ Δ i>d ])) []) 128 | 129 | 130 | -- helper function for converting definitions or constructors to 131 | -- proof terms. 132 | fromDefOrCon : (s : Name) → ∃[ n ] List (PsTerm n) → ∃ PsTerm 133 | fromDefOrCon f (n , ts) = n , con (name f) ts 134 | 135 | 136 | -- specialised function to convert literals of natural numbers 137 | -- (since they have a representation using Agda names) 138 | convertℕ : ∀ {k} → ℕ → PsTerm k 139 | convertℕ zero = con (name (quote zero)) [] 140 | convertℕ (suc n) = con (name (quote suc)) (convertℕ n ∷ []) 141 | 142 | 143 | -- convert an Agda term to a term, abstracting over the treatment of 144 | -- variables with an explicit dictionary of the type `ConvertVar`--- 145 | -- passing in `ConvertVar4Term` or `ConvertVar4Goal` will result in 146 | -- rule-terms or goal-terms, respectively. 147 | 148 | convert : ConvertVar → (depth : ℕ) → AgTerm → Error (∃ PsTerm) 149 | convertChildren : 150 | ConvertVar → ℕ → List (Arg AgTerm) → Error (∃[ n ] List (PsTerm n)) 151 | 152 | 153 | convert cv d (lit (nat n)) = inj₂ (0 , convertℕ n) 154 | convert cv d (lit l) = inj₂ (0 , lit l) 155 | convert cv d (var i []) = inj₂ (cv d i) 156 | convert cv d (var i args) = inj₁ unsupportedSyntax 157 | convert cv d (con c args) = fromDefOrCon c ⟨$⟩ convertChildren cv d args 158 | convert cv d (def f args) = fromDefOrCon f ⟨$⟩ convertChildren cv d args 159 | convert cv d (pi (arg (arg-info visible _) t₁) (abs _ t₂)) with convert cv d t₁ | convert cv (suc d) t₂ 160 | ... | inj₁ msg | _ = inj₁ msg 161 | ... | _ | inj₁ msg = inj₁ msg 162 | ... | inj₂ (n₁ , p₁) | inj₂ (n₂ , p₂) 163 | with match p₁ p₂ 164 | ... | (p₁′ , p₂′) = inj₂ (n₁ ⊔ n₂ , con impl (p₁′ ∷ p₂′ ∷ [])) 165 | convert cv d (pi (arg _ _) (abs _ t₂)) = convert cv (suc d) t₂ 166 | convert cv d (lam _ _) = inj₁ unsupportedSyntax 167 | convert cv d (pat-lam _ _) = inj₁ unsupportedSyntax 168 | convert cv d (sort _) = inj₁ unsupportedSyntax 169 | convert cv d unknown = inj₁ unsupportedSyntax 170 | convert cv d (meta x args) = inj₁ unsupportedSyntax 171 | 172 | 173 | convertChildren cv d [] = inj₂ (0 , []) 174 | convertChildren cv d (arg (arg-info visible _) t ∷ ts) 175 | with convert cv d t | convertChildren cv d ts 176 | ... | inj₁ msg | _ = inj₁ msg 177 | ... | _ | inj₁ msg = inj₁ msg 178 | ... | inj₂ (m , p) | inj₂ (n , ps) with match p ps 179 | ... | (p′ , ps′) = inj₂ (m ⊔ n , p′ ∷ ps′) 180 | convertChildren cv d (arg _ _ ∷ ts) = convertChildren cv d ts 181 | 182 | 183 | -- convert an Agda term to a rule-term. 184 | agda2term : AgTerm → Error (∃ PsTerm) 185 | agda2term t = convert convertVar4Term 0 t 186 | 187 | 188 | -- split a term at every occurrence of the `impl` constructor--- 189 | -- equivalent to splitting at every occurrence of the _→_ symbol in 190 | -- an Agda term. 191 | split : ∀ {n} → PsTerm n → ∃[ k ] Vec (PsTerm n) (suc k) 192 | split (con impl (t₁ ∷ t₂ ∷ [])) = Prod.map suc (λ ts → t₁ ∷ ts) (split t₂) 193 | split t = zero , t ∷ [] 194 | 195 | 196 | -- convert an Agda term to a goal-term, together with a `HintDB` 197 | -- representing the premises of the rule---this means that for a 198 | -- term of the type `A → B` this function will generate a goal of 199 | -- type `B` and a premise of type `A`. 200 | agda2goal×premises : AgTerm → Error (∃ PsTerm × Rules) 201 | agda2goal×premises t with convert convertVar4Goal 0 t 202 | ... | inj₁ msg = inj₁ msg 203 | ... | inj₂ (n , p) with split p 204 | ... | (k , ts) with initLast ts 205 | ... | (prems , goal , _) = inj₂ ((n , goal) , toPremises (pred k) prems) 206 | where 207 | toPremises : ∀ {k} → ℕ → Vec (PsTerm n) k → Rules 208 | toPremises i [] = [] 209 | toPremises i (t ∷ ts) = (n , rule (var i) t []) ∷ toPremises (pred i) ts 210 | 211 | 212 | 213 | 214 | -- convert an Agda name to an rule-term. 215 | name2term : Name → TC (Error (∃ PsTerm)) 216 | name2term nm = bindTC (getType nm) (λ tp → returnTC (agda2term tp)) 217 | 218 | 219 | 220 | -- convert an Agda name to a rule. 221 | name2ruleHelper : Name → (Error (∃ PsTerm)) → TC (Error (∃ Rule)) 222 | name2ruleHelper nm name2term_nm with name2term_nm 223 | ... | inj₁ msg = returnTC (inj₁ msg) 224 | ... | inj₂ (n , t) with split t 225 | ... | (k , ts) with initLast ts 226 | ... | (prems , concl , _) = returnTC (inj₂ (n , rule (name nm) concl (toList prems))) 227 | 228 | 229 | 230 | -- convert an Agda name to a rule. 231 | name2rule : Name → TC (Error (∃ Rule)) 232 | name2rule nm = bindTC (name2term nm) (name2ruleHelper nm) 233 | 234 | 235 | 236 | -- function which reifies untyped proof terms (from the 237 | -- `ProofSearch` module) to untyped Agda terms. 238 | 239 | 240 | reify : Proof → TC AgTerm 241 | reifyChildren : List Proof → TC (List (Arg AgTerm)) 242 | 243 | reify (con (var i) ps) = returnTC ((var i [])) 244 | reify (con (name n) ps) = 245 | bindTC (getDefinition n) (λ 246 | { (function x) → bindTC (reifyChildren ps) (λ rc → returnTC (def n rc)) 247 | ; (data-type pars cs) → bindTC (reifyChildren ps) ((λ rc → returnTC (con n rc))) 248 | ; (record′ c _) → returnTC unknown 249 | ; (constructor′ d ) → returnTC unknown 250 | ; axiom → returnTC unknown 251 | ; primitive′ → returnTC unknown} ) 252 | 253 | 254 | reifyChildren [] = returnTC [] 255 | reifyChildren (p ∷ ps) = 256 | bindTC (reify p) 257 | (λ rp → bindTC (reifyChildren ps) (λ rcps → returnTC (toArg rp ∷ rcps))) 258 | where 259 | toArg : AgTerm → Arg AgTerm 260 | toArg = arg (arg-info visible relevant) 261 | 262 | 263 | 264 | -- data-type `Exception` which is used to unquote error messages to 265 | -- the type-level so that `auto` can generate descriptive type-errors. 266 | 267 | data Exception : Message → Set where 268 | throw : (msg : Message) → Exception msg 269 | 270 | quoteError : Message → AgTerm 271 | quoteError (searchSpaceExhausted) = quoteTerm (throw searchSpaceExhausted) 272 | quoteError (unsupportedSyntax) = quoteTerm (throw unsupportedSyntax) 273 | -------------------------------------------------------------------------------- /src/Auto/Counting.agda: -------------------------------------------------------------------------------- 1 | open import Auto.Core using (Rule; RuleName; _≟-RuleName_; name2rule; IsHintDB) 2 | open import Level using (zero) 3 | open import Function using (id; _∘_) 4 | open import Category.Functor using (module RawFunctor) 5 | open import Data.Bool as Bool using (if_then_else_) 6 | open import Data.List as List using (List; _++_; []; [_]) 7 | open import Data.Maybe as Maybe using (Maybe; just; nothing) 8 | open import Data.Nat as Nat using (ℕ; pred) 9 | open import Data.Product as Prod using (∃; _,_; proj₂) 10 | open import Data.Sum as Sum using (_⊎_; inj₁; inj₂) 11 | open import Data.Unit as Unit using (⊤) 12 | open import Reflection using (Name) 13 | open import Relation.Nullary.Decidable using (⌊_⌋) 14 | 15 | module Auto.Counting where 16 | 17 | 18 | -------------------------------------------------------------------------------- 19 | -- Define a 'counting' hint database which, upon selection of a rule will -- 20 | -- decrement an associated 'count' value, and upon reaching 0 will delete -- 21 | -- the hint from the hint database. -- 22 | -- -- 23 | -- The count values can either be natural numbers, in which case they -- 24 | -- will be decremented as expected, or the value ⊤, in which case they -- 25 | -- will not be decremented, effectively inserting infinite copies of the -- 26 | -- rule into the hint database. -- 27 | -------------------------------------------------------------------------------- 28 | 29 | module CountingHintDB where 30 | 31 | open RawFunctor (Maybe.functor {zero}) using (_<$>_) 32 | 33 | Count : Set 34 | Count = ℕ ⊎ ⊤ 35 | 36 | record Hint (k : ℕ) : Set where 37 | constructor mkHint 38 | field 39 | rule : Rule k 40 | count : Count 41 | 42 | ruleName : RuleName 43 | ruleName = Rule.name rule 44 | 45 | HintDB : Set 46 | HintDB = List (∃ Hint) 47 | 48 | decrCount : ∀ {k} → Hint k → Maybe (Hint k) 49 | decrCount {k} (mkHint r c) = mkHint r <$> decrCount′ c 50 | where 51 | decrCount′ : Count → Maybe Count 52 | decrCount′ (inj₁ 0) = nothing 53 | decrCount′ (inj₁ 1) = nothing 54 | decrCount′ (inj₁ x) = just (inj₁ (pred x)) 55 | decrCount′ (inj₂ _) = just (inj₂ _) 56 | 57 | getTr : ∀ {k} → Hint k → (HintDB → HintDB) 58 | getTr h₁ = List.concatMap (List.fromMaybe ∘ mdecr₁) 59 | where 60 | mdecr₁ : ∃ Hint → Maybe (∃ Hint) 61 | mdecr₁ (_ , h₂) = 62 | if ⌊ Hint.ruleName h₁ ≟-RuleName Hint.ruleName h₂ ⌋ 63 | then (_,_ _) <$> decrCount h₂ 64 | else just (_ , h₂) 65 | 66 | countingHintDB : IsHintDB 67 | countingHintDB = record 68 | { HintDB = HintDB 69 | ; Hint = Hint 70 | ; getHints = id 71 | ; getRule = Hint.rule 72 | ; getTr = getTr 73 | ; ε = [] 74 | ; _∙_ = _++_ 75 | ; return = λ r → [ _ , mkHint r (inj₂ _) ] 76 | } 77 | 78 | 79 | 80 | open CountingHintDB using (mkHint; countingHintDB) 81 | open import Auto.Extensible countingHintDB public 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- 86 | -- Define some new syntax in order to insert rules with limited usage. -- 87 | -------------------------------------------------------------------------------- 88 | 89 | infixl 5 _<<[_]_ 90 | 91 | _<<[_]_ : HintDB → ℕ → Name → HintDB 92 | db <<[ 0 ] _ = db 93 | db <<[ x ] n with (name2rule n) 94 | db <<[ x ] n | inj₁ msg = db 95 | db <<[ x ] n | inj₂ (k , r) = db ∙ [ k , mkHint r (inj₁ x) ] 96 | 97 | -------------------------------------------------------------------------------- /src/Auto/Example/Even.agda: -------------------------------------------------------------------------------- 1 | open import Auto 2 | open import Algebra 3 | open import Data.List using (_∷_; [];_++_;List) 4 | open import Data.Nat using (ℕ; suc; zero; _+_) 5 | open import Data.Product using (_×_; ∃₂; proj₁; proj₂) 6 | open import Relation.Binary.PropositionalEquality as PropEq using (_≡_; refl; cong; sym) 7 | open import Reflection 8 | 9 | module Auto.Example.Even where 10 | 11 | private 12 | n+0≡n : ∀ n → n + 0 ≡ n 13 | n+0≡n zero = refl 14 | n+0≡n (suc n) = cong suc (n+0≡n n) 15 | 16 | m+1+n≡1+m+n : ∀ m n → m + suc n ≡ suc (m + n) 17 | m+1+n≡1+m+n zero n = refl 18 | m+1+n≡1+m+n (suc m) n = cong suc (m+1+n≡1+m+n m n) 19 | 20 | 21 | data Even : ℕ → Set where 22 | isEven0 : Even 0 23 | isEven+2 : ∀ {n} → Even n → Even (suc (suc n)) 24 | 25 | even+ : ∀ {n m} → Even n → Even m → Even (n + m) 26 | even+ isEven0 e2 = e2 27 | even+ (isEven+2 e1) e2 = isEven+2 (even+ e1 e2) 28 | 29 | isEven-2 : ∀ {n} → Even (2 + n) → Even n 30 | isEven-2 (isEven+2 n) = n 31 | 32 | simple : ∀ {n} → Even n → Even (n + 2) 33 | simple e = even+ e (isEven+2 isEven0) 34 | 35 | rules : HintDB 36 | rules = [] << quote isEven0 37 | << quote isEven+2 38 | << quote even+ 39 | 40 | test₁ : Even 4 41 | test₁ = tactic (auto 5 rules) 42 | 43 | test₂ : ∀ {n} → Even n → Even (n + 2) 44 | test₂ = tactic (auto 5 rules) 45 | 46 | test₃ : ∀ {n} → Even n → Even (4 + n) 47 | test₃ = tactic (auto 5 rules) 48 | 49 | test₄ : ∀ {n} → Even n → Even (n + 2) 50 | test₄ = tactic (auto 5 rules) 51 | 52 | -- attempting to prove an impossible goal (e.g. evenness of n + 3 53 | -- for all n) will result in searchSpaceExhausted 54 | goal₁ = quoteTerm (∀ {n} → Even n → Even (n + 3)) 55 | fail₁ : unquote (auto 5 rules goal₁) ≡ throw searchSpaceExhausted 56 | fail₁ = refl 57 | 58 | -- attempting to convert an unsupported expression (e.g. a lambda 59 | -- term) will result in unsupportedSyntax 60 | goal₂ = quoteTerm (∃₂ λ m n → Even (m + n)) 61 | fail₂ : unquote (auto 5 rules goal₂) ≡ throw unsupportedSyntax 62 | fail₂ = refl 63 | -------------------------------------------------------------------------------- /src/Auto/Example/Sublists.agda: -------------------------------------------------------------------------------- 1 | open import Function using (flip; _∘_; _$_) 2 | open import Auto.Counting 3 | open import Data.Nat using (ℕ) 4 | open import Data.List using (List; _∷_; []) 5 | open import Data.Product using (∃; _,_; proj₂) 6 | open import Data.Maybe 7 | open import Data.Sum using (inj₁; inj₂; isInj₂) 8 | 9 | module Auto.Example.Sublists where 10 | 11 | 12 | infix 3 _⊆_ 13 | 14 | data _⊆_ {a} {A : Set a} : List A → List A → Set a where 15 | stop : [] ⊆ [] 16 | drop : ∀ {xs y ys} → xs ⊆ ys → xs ⊆ y ∷ ys 17 | keep : ∀ {x xs ys} → xs ⊆ ys → x ∷ xs ⊆ x ∷ ys 18 | 19 | 20 | refl : ∀ {a} {A : Set a} {xs : List A} → xs ⊆ xs 21 | refl {xs = []} = stop 22 | refl {xs = x ∷ xs} = keep refl 23 | 24 | trans : ∀ {a} {A : Set a} {xs ys zs : List A} → xs ⊆ ys → ys ⊆ zs → xs ⊆ zs 25 | trans p stop = p 26 | trans p (drop q) = drop (trans p q) 27 | trans (drop p) (keep q) = drop (trans p q) 28 | trans (keep p) (keep q) = keep (trans p q) 29 | 30 | hintdb₁ : HintDB 31 | hintdb₁ = ε << quote drop 32 | << quote keep 33 | << quote trans 34 | 35 | lemma₁ : {ws xs ys zs : List ℕ} 36 | → ws ⊆ 1 ∷ xs → xs ⊆ ys → ys ⊆ zs → ws ⊆ 1 ∷ 2 ∷ zs 37 | lemma₁ = tactic (auto dfs 10 hintdb₁) 38 | 39 | lemma₂ : {ws xs ys zs : List ℕ} 40 | → ws ⊆ 1 ∷ xs → xs ⊆ ys → ys ⊆ zs → ws ⊆ 2 ∷ zs 41 | lemma₂ = tactic (auto dfs 10 hintdb₁) 42 | 43 | 44 | {- 45 | db₂ : HintDB 46 | db₂ = ε << quote trans 47 | << quote keep 48 | << quote drop 49 | 50 | test₂ : {A : Set} {ws xs ys zs : List A} → ws ⊆ xs → ys ⊆ zs → ws ⊆ zs 51 | test₂ = tactic (auto dfs 10 db₁) 52 | -} 53 | -------------------------------------------------------------------------------- /src/Auto/Example/TypeClasses.agda: -------------------------------------------------------------------------------- 1 | open import Auto 2 | open import Function using (const) 3 | open import Data.Bool using (Bool; true; false) 4 | open import Data.Bool.Show as Bool using () 5 | open import Data.List using (_∷_; []) 6 | open import Data.Maybe 7 | open import Data.Nat using (ℕ; suc; zero) 8 | open import Data.Nat.Show as Nat using () 9 | open import Data.String using (String; _++_) 10 | open import Data.Sum renaming (_⊎_ to Either; inj₁ to left; inj₂ to right) 11 | open import Relation.Binary.PropositionalEquality using (_≡_; refl) 12 | 13 | module Auto.Example.TypeClasses where 14 | 15 | -------------------------------------------------------------------------------- 16 | -- * We can construct a class for the Show function (as a dependent record) * -- 17 | -------------------------------------------------------------------------------- 18 | 19 | record Show (A : Set) : Set where 20 | constructor mkShow 21 | field 22 | show : A → String 23 | 24 | open Show {{...}} 25 | 26 | 27 | -------------------------------------------------------------------------------- 28 | -- * And set up a list of rules which guide the instance resolution * -- 29 | -------------------------------------------------------------------------------- 30 | 31 | rules : HintDB 32 | rules = [] << quote instShowEither << quote instShowBool << quote instShowNat 33 | where 34 | instShowBool : Show Bool 35 | instShowBool = mkShow Bool.show 36 | 37 | instShowNat : Show ℕ 38 | instShowNat = mkShow Nat.show 39 | 40 | instShowEither : {A B : Set} → Show A → Show B → Show (Either A B) 41 | instShowEither {A} {B} instShowA instShowB = mkShow showEither 42 | where 43 | showEither : Either A B → String 44 | showEither (left x) = "left " ++ show x 45 | showEither (right y) = "right " ++ show y 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- 50 | -- * Using these rules and `auto` we can easily and robustly compute the * -- 51 | -- * instances we need. * -- 52 | -------------------------------------------------------------------------------- 53 | 54 | example₁ : String 55 | example₁ = show (left true) ++ show (right 4) 56 | where 57 | instance 58 | inst : Show (Either Bool ℕ) 59 | inst = tactic (auto 5 rules) 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- 64 | -- * This fails due to normalisation from the non-dependent pair _×_ to the * -- 65 | -- * dependent pair Σ (as `A × B` is defined as `Σ A (λ _ → B)`). * -- 66 | -------------------------------------------------------------------------------- 67 | module DefaultPair where 68 | 69 | open import Data.Product using (_×_; _,_) 70 | 71 | instShowPair : {A B : Set} → Show A → Show B → Show (A × B) 72 | instShowPair {A} {B} showA showB = record { show = showPair } 73 | where 74 | showPair : A × B → String 75 | showPair (proj₁ , proj₂) = show proj₁ ++ "," ++ show proj₂ 76 | 77 | inst : Exception unsupportedSyntax 78 | inst = unquote (auto 5 (rules << quote instShowPair) g) 79 | where 80 | g = quoteTerm (Show (Bool × ℕ)) 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- 85 | -- * So we're forced to use a custom pair, which isn't derived from * -- 86 | -- * a dependent pair * -- 87 | -------------------------------------------------------------------------------- 88 | module CustomPair where 89 | 90 | data _×_ (A B : Set) : Set where 91 | _,_ : A → B → A × B 92 | 93 | instShowPair : ∀ {A B} → Show A → Show B → Show (A × B) 94 | instShowPair {A} {B} showA showB = record { show = showPair } 95 | where 96 | showPair : A × B → String 97 | showPair (proj₁ , proj₂) = show proj₁ ++ "," ++ show proj₂ 98 | 99 | example₂ : String 100 | example₂ = show (true , 1) 101 | where 102 | instance 103 | inst : Show (Bool × ℕ) 104 | inst = tactic (auto 5 (rules << quote instShowPair)) 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- 109 | -- * This fails due to something super weird which I haven't encountered * -- 110 | -- * before at all... * -- 111 | -------------------------------------------------------------------------------- 112 | module AbstractPair where 113 | 114 | open import Data.Product as Σ using (Σ) 115 | 116 | abstract 117 | _×_ : (A B : Set) → Set 118 | A × B = Σ A (const B) 119 | 120 | instShowPair : ∀ {A B} → Show A → Show B → Show (A × B) 121 | instShowPair {A} {B} showA showB = record { show = showPair } 122 | where 123 | showPair : A × B → String 124 | showPair (proj₁ Σ., proj₂) = show proj₁ ++ "," ++ show proj₂ 125 | 126 | _,_ : {A B : Set} (x : A) (y : B) → A × B 127 | _,_ = Σ._,_ 128 | 129 | --inst : Show (Bool × ℕ) 130 | --inst = tactic (auto 5 (rules << quote instShowPair)) 131 | -------------------------------------------------------------------------------- /src/Auto/Extensible.agda: -------------------------------------------------------------------------------- 1 | open import Auto.Core 2 | open import Data.List using (_∷_; []; length) 3 | open import Data.Nat using (ℕ; zero; suc) 4 | open import Data.Product using (_,_) 5 | open import Data.Sum using (inj₁; inj₂) 6 | open import Reflection using (Term; Name; lam; visible; abs; TC; returnTC; bindTC) 7 | 8 | module Auto.Extensible (instHintDB : IsHintDB) where 9 | 10 | 11 | open IsHintDB instHintDB public 12 | open PsExtensible instHintDB public 13 | open Auto.Core public using (dfs; bfs; Exception; throw; searchSpaceExhausted; unsupportedSyntax) 14 | 15 | 16 | auto : Strategy → ℕ → HintDB → Term → TC Term 17 | auto search depth db type 18 | with agda2goal×premises type 19 | ... | inj₁ msg = returnTC (quoteError msg) 20 | ... | inj₂ ((n , g) , args) 21 | with search (suc depth) (solve g (fromRules args ∙ db)) 22 | ... | [] = returnTC (quoteError searchSpaceExhausted) 23 | ... | (p ∷ _) = bindTC (reify p) (λ rp → returnTC (intros rp)) 24 | where 25 | intros : Term → Term 26 | intros = introsAcc (length args) 27 | where 28 | introsAcc : ℕ → Term → Term 29 | introsAcc zero t = t 30 | introsAcc (suc k) t = lam visible (abs "TODO" (introsAcc k t)) 31 | 32 | 33 | 34 | infixl 5 _<<_ 35 | 36 | _<<_ : HintDB → Name → TC HintDB 37 | db << n = bindTC (name2rule n) (λ 38 | {(inj₁ msg) → returnTC db 39 | ; (inj₂ (k , r)) → returnTC (db ∙ return r)}) 40 | -- db << n with (name2rule n) 41 | -- db << n | inj₁ msg = db 42 | -- db << n | inj₂ (k , r) = db ∙ return r 43 | -------------------------------------------------------------------------------- /src/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Pepijn Kokke, Wouter Swierstra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/ProofSearch.agda: -------------------------------------------------------------------------------- 1 | open import Level using (_⊔_) 2 | open import Algebra using (module CommutativeSemiring; module DistributiveLattice) 3 | open import Function using (id; const; _∘_; _$_) 4 | open import Coinduction using (∞; ♯_; ♭) 5 | open import Data.Fin as Fin using (Fin; suc; zero) 6 | open import Data.List as List using (List; _∷_; []; [_]; _++_; length; concat; foldr; concatMap) 7 | open import Data.Maybe using (Maybe; just; nothing) 8 | open import Data.Nat using (ℕ; suc; zero; _≤_; z≤n; s≤s) 9 | open import Data.Nat.Properties using (commutativeSemiring; distributiveLattice; ≤-decTotalOrder) 10 | open import Data.Product using (∃; _×_; _,_) 11 | open import Data.Sum using (_⊎_; inj₁; inj₂) 12 | open import Data.Vec as Vec using (Vec; _∷_; []; fromList) 13 | open import Relation.Nullary using (Dec; yes; no) 14 | open import Relation.Binary using (module DecTotalOrder) 15 | open import Relation.Binary.PropositionalEquality using (_≡_; refl; sym; trans; cong; subst) 16 | 17 | 18 | module ProofSearch 19 | (RuleName : Set) 20 | (TermName : Set) (_≟-TermName_ : (x y : TermName) → Dec (x ≡ y)) 21 | (Literal : Set) (_≟-Literal_ : (x y : Literal) → Dec (x ≡ y)) 22 | where 23 | 24 | open import Unification TermName _≟-TermName_ Literal _≟-Literal_ public hiding (_++_) 25 | 26 | 27 | ---------------------------------------------------------------------------- 28 | -- * define rules and utility functions * -- 29 | ---------------------------------------------------------------------------- 30 | 31 | private 32 | ∃-syntax : ∀ {a b} {A : Set a} → (A → Set b) → Set (b ⊔ a) 33 | ∃-syntax = ∃ 34 | syntax ∃-syntax (λ x → B) = ∃[ x ] B 35 | 36 | -- introduce rules 37 | record Rule (n : ℕ) : Set where 38 | constructor rule 39 | field 40 | name : RuleName 41 | conclusion : Term n 42 | premises : List (Term n) 43 | 44 | open Rule using (name; conclusion; premises) 45 | 46 | 47 | -- alias for list of rules 48 | Rules : Set 49 | Rules = List (∃ Rule) 50 | 51 | 52 | -- compute the arity of a rule 53 | arity : ∀ {n} (r : Rule n) → ℕ 54 | arity = length ∘ premises 55 | 56 | 57 | -- open instances relevant to definitions of difference, inject and raise 58 | open CommutativeSemiring commutativeSemiring using (_+_; +-comm) 59 | open DistributiveLattice distributiveLattice using (_∧_; ∧-comm) 60 | open DecTotalOrder ≤-decTotalOrder using (total) 61 | 62 | 63 | -- compute the difference between two natural numbers, given an 64 | -- ordering between them. 65 | Δ_ : ∀ {m n} → m ≤ n → ℕ 66 | Δ z≤n {k} = k 67 | Δ s≤s p = Δ p 68 | 69 | -- correctness proof of the difference operator Δ. 70 | Δ-correct : ∀ {m n} (p : m ≤ n) → n ≡ m + Δ p 71 | Δ-correct z≤n = refl 72 | Δ-correct (s≤s p) = cong suc (Δ-correct p) 73 | 74 | -- type class for injections in the fashion of Fin.inject+ 75 | record Inject (T : ℕ → Set) : Set where 76 | field 77 | inject : ∀ {m} n → T m → T (m + n) 78 | 79 | inject≤ : ∀ {m n} → m ≤ n → T m → T n 80 | inject≤ {m} {n} p t = subst T (sym (Δ-correct p)) (inject (Δ p) t) 81 | 82 | open Inject {{...}} using (inject; inject≤) 83 | 84 | 85 | -- type class for raising in the fashion of Fin.raise 86 | record Raise (T : ℕ → Set) : Set where 87 | field 88 | raise : ∀ {m} n → T m → T (n + m) 89 | 90 | raise≤ : ∀ {m n} → m ≤ n → T m → T n 91 | raise≤ {m} {n} p t = subst T (sym (trans (Δ-correct p) (+-comm m (Δ p)))) (raise (Δ p) t) 92 | 93 | open Raise {{...}} using (raise; raise≤) 94 | 95 | 96 | -- instances for inject/raise for all used data types 97 | instance 98 | InjectFin : Inject Fin 99 | InjectFin = record { inject = Fin.inject+ } 100 | RaiseFin : Raise Fin 101 | RaiseFin = record { raise = Fin.raise } 102 | InjectTerm : Inject Term 103 | InjectTerm = record { inject = λ n → replace (var ∘ inject n) } 104 | RaiseTerm : Raise Term 105 | RaiseTerm = record { raise = λ m → replace (var ∘ raise m) } 106 | InjectTerms : Inject (List ∘ Term) 107 | InjectTerms = record { inject = λ n → List.map (inject n) } 108 | RaiseTerms : Raise (List ∘ Term) 109 | RaiseTerms = record { raise = λ m → List.map (raise m) } 110 | InjectGoals : ∀ {k} → Inject (λ n → Vec (Term n) k) 111 | InjectGoals = record { inject = λ n → Vec.map (inject n) } 112 | RaiseGoals : ∀ {k} → Raise (λ n → Vec (Term n) k) 113 | RaiseGoals = record { raise = λ m → Vec.map (raise m) } 114 | InjectRule : Inject Rule 115 | InjectRule = record { inject = λ n → λ { (rule nm c p) → rule nm (inject n c) (inject n p) } } 116 | RaiseRule : Raise Rule 117 | RaiseRule = record { raise = λ m → λ { (rule nm c p) → rule nm (raise m c) (raise m p) } } 118 | 119 | -- could rephrase inject/raise in terms of allowing modification by 120 | -- a function ℕ → ℕ, but really... why would I... it makes all the 121 | -- other instances much, much more obtuse 122 | injectSubst : ∀ {m n} (δ : ℕ) → Subst m n → Subst (m + δ) (n + δ) 123 | injectSubst _ nil = nil 124 | injectSubst δ (snoc s t x) = snoc (injectSubst δ s) (inject δ t) (inject δ x) 125 | 126 | 127 | private 128 | m≤n→m⊔n=n : ∀ {m n} → m ≤ n → m ∧ n ≡ n 129 | m≤n→m⊔n=n z≤n = refl 130 | m≤n→m⊔n=n (s≤s p) = cong suc (m≤n→m⊔n=n p) 131 | 132 | 133 | -- match indices of injectable data types 134 | match : ∀ {m n} {I J} {{i : Inject I}} {{j : Inject J}} 135 | → I m → J n → I (m ∧ n) × J (m ∧ n) 136 | match {m} {n} i j with total m n 137 | ... | inj₁ p rewrite m≤n→m⊔n=n p = (inject≤ p i , j) 138 | ... | inj₂ p rewrite ∧-comm m n | m≤n→m⊔n=n p = (i , inject≤ p j) 139 | 140 | 141 | ---------------------------------------------------------------------------- 142 | -- * define hint databases * -- 143 | ---------------------------------------------------------------------------- 144 | 145 | record IsHintDB : Set₁ where 146 | field 147 | HintDB : Set 148 | Hint : ℕ → Set 149 | 150 | Hints : Set 151 | Hints = List (∃ Hint) 152 | 153 | field 154 | getHints : HintDB → Hints 155 | getRule : ∀ {k} → Hint k → Rule k 156 | getTr : ∀ {k} → Hint k → (HintDB → HintDB) 157 | ε : HintDB 158 | _∙_ : HintDB → HintDB → HintDB 159 | return : ∀ {k} → Rule k → HintDB 160 | 161 | fromRules : Rules → HintDB 162 | fromRules [] = ε 163 | fromRules ((k , r) ∷ rs) = return r ∙ fromRules rs 164 | 165 | 166 | ---------------------------------------------------------------------------- 167 | -- * define simple hint databases * -- 168 | ---------------------------------------------------------------------------- 169 | 170 | simpleHintDB : IsHintDB 171 | simpleHintDB = record 172 | { HintDB = Rules 173 | ; Hint = Rule 174 | ; getHints = id 175 | ; getRule = id 176 | ; getTr = const id 177 | ; ε = [] 178 | ; _∙_ = _++_ 179 | ; return = λ r → [ _ , r ] 180 | } 181 | 182 | 183 | ---------------------------------------------------------------------------- 184 | -- * define search trees, proofs and partial proofs * -- 185 | ---------------------------------------------------------------------------- 186 | 187 | -- simple alias to set apart the goal term 188 | Goal = Term 189 | 190 | -- search trees 191 | data SearchTree (A : Set) : Set where 192 | leaf : A → SearchTree A 193 | node : List (∞ (SearchTree A)) → SearchTree A 194 | 195 | -- representation of a failed branch 196 | fail : ∀ {A} → SearchTree A 197 | fail = node [] 198 | 199 | data Proof : Set where 200 | con : (name : RuleName) (args : List Proof) → Proof 201 | 202 | -- representation of an incomplete proof 203 | Proof′ : ℕ → Set 204 | Proof′ m = ∃[ k ] (Vec (Goal m) k × (Vec Proof k → Proof)) 205 | 206 | con′ : ∀ {n k} (r : Rule n) → Vec Proof (arity r + k) → Vec Proof (suc k) 207 | con′ {n} {k} r xs = head ∷ rest 208 | where 209 | head : Proof 210 | head = con (name r) (Vec.toList $ Vec.take (arity r) xs) 211 | rest : Vec Proof k 212 | rest = Vec.drop (arity r) xs 213 | 214 | 215 | ---------------------------------------------------------------------------- 216 | -- * define proof search function * -- 217 | ---------------------------------------------------------------------------- 218 | 219 | module Extensible (isHintDB : IsHintDB) where 220 | 221 | open IsHintDB isHintDB 222 | 223 | solve : ∀ {m} → Goal m → HintDB → SearchTree Proof 224 | solve {m} g = solveAcc {m} (1 , g ∷ [] , Vec.head) 225 | where 226 | solveAcc : ∀ {m} → Proof′ m → HintDB → SearchTree Proof 227 | solveAcc {m} (0 , [] , p) _ = leaf (p []) 228 | solveAcc {m} (suc k , g ∷ gs , p) db = node (steps (getHints db)) 229 | where 230 | step : ∃[ δ ] (Hint δ) → ∞ (SearchTree Proof) 231 | step (δ , h) with unify (inject δ g) (raise m (conclusion (getRule h))) 232 | ... | nothing = ♯ node [] -- fail 233 | ... | just (n , mgu) = ♯ solveAcc {n} prf (getTr h db) 234 | where 235 | prf : Proof′ n 236 | prf = arity (getRule h) + k , gs′ , (p ∘ con′ (getRule h)) 237 | where 238 | prm′ = raise m (Vec.fromList (premises (getRule h))) 239 | gs′ = Vec.map (replace (sub mgu)) (prm′ Vec.++ inject δ gs) 240 | 241 | 242 | -- equivalent to `map step` due to termination checker 243 | steps : List (∃ Hint) → List (∞ (SearchTree Proof)) 244 | steps [] = [] 245 | steps (h ∷ hs) = step h ∷ steps hs 246 | 247 | ---------------------------------------------------------------------------- 248 | -- * define various search strategies * -- 249 | ---------------------------------------------------------------------------- 250 | 251 | Strategy : Set₁ 252 | Strategy = ∀ {A} (depth : ℕ) → SearchTree A → List A 253 | 254 | dfs : Strategy 255 | dfs zero _ = [] 256 | dfs (suc k) (leaf x) = x ∷ [] 257 | dfs (suc k) (node xs) = concatMap (λ x → dfs k (♭ x)) xs 258 | 259 | 260 | bfs : Strategy 261 | bfs depth t = concat (Vec.toList (bfsAcc depth t)) 262 | where 263 | merge : ∀ {A : Set} {k} → (xs ys : Vec (List A) k) → Vec (List A) k 264 | merge [] [] = [] 265 | merge (x ∷ xs) (y ∷ ys) = (x ++ y) ∷ merge xs ys 266 | 267 | empty : ∀ {A : Set} {k} → Vec (List A) k 268 | empty {k = zero} = [] 269 | empty {k = suc k} = [] ∷ empty 270 | 271 | bfsAcc : ∀ {A} (depth : ℕ) → SearchTree A → Vec (List A) depth 272 | bfsAcc zero _ = [] 273 | bfsAcc (suc k) (leaf x) = (x ∷ []) ∷ empty 274 | bfsAcc (suc k) (node xs) = [] ∷ foldr merge empty (List.map (λ x → bfsAcc k (♭ x)) xs) 275 | -------------------------------------------------------------------------------- /src/Unification.agda: -------------------------------------------------------------------------------- 1 | open import Function using (_∘_) 2 | open import Category.Monad using (module RawMonad) 3 | open import Data.Fin using (Fin; suc; zero) 4 | open import Data.Fin.Properties renaming (_≟_ to _≟-Fin_) 5 | open import Data.List as List using (List; _∷_; []) 6 | open import Data.List.Properties renaming (∷-injective to ∷-inj) 7 | open import Data.Maybe as Maybe using (Maybe; just; nothing) 8 | open import Data.Nat using (ℕ; suc; zero) 9 | open import Data.Product as Prod using (∃; _×_; _,_; proj₁; proj₂) 10 | open import Relation.Nullary using (Dec; yes; no) 11 | open import Relation.Binary.PropositionalEquality using (_≡_; refl; cong; cong₂) 12 | 13 | module Unification 14 | (Name : Set) (_≟-Name_ : (x y : Name) → Dec (x ≡ y)) 15 | (Literal : Set) (_≟-Literal_ : (x y : Literal) → Dec (x ≡ y)) 16 | where 17 | 18 | open RawMonad {{...}} using (_<$>_; _>>=_; return) 19 | 20 | private 21 | instance 22 | MaybeMonad = Maybe.monad 23 | 24 | data Term (n : ℕ) : Set where 25 | var : (x : Fin n) → Term n 26 | con : (s : Name) (ts : List (Term n)) → Term n 27 | lit : (l : Literal) → Term n 28 | -- ext : (x : Fin (suc n)) (t : Term (suc n)) → Term n 29 | 30 | var-inj : ∀ {n x₁ x₂} → var {n} x₁ ≡ var {n} x₂ → x₁ ≡ x₂ 31 | var-inj refl = refl 32 | 33 | con-inj : ∀ {n s₁ s₂ ts₁ ts₂} → con {n} s₁ ts₁ ≡ con {n} s₂ ts₂ → s₁ ≡ s₂ × ts₁ ≡ ts₂ 34 | con-inj refl = (refl , refl) 35 | 36 | lit-inj : ∀ {n x₁ x₂} → lit {n} x₁ ≡ lit {n} x₂ → x₁ ≡ x₂ 37 | lit-inj refl = refl 38 | 39 | mutual 40 | _≟-Term_ : ∀ {n} → (t₁ t₂ : Term n) → Dec (t₁ ≡ t₂) 41 | _≟-Term_ (var _) (lit _) = no (λ ()) 42 | _≟-Term_ (var _) (con _ _) = no (λ ()) 43 | _≟-Term_ (var x₁) (var x₂) with x₁ ≟-Fin x₂ 44 | ... | yes x₁=x₂ = yes (cong var x₁=x₂) 45 | ... | no x₁≠x₂ = no (x₁≠x₂ ∘ var-inj) 46 | _≟-Term_ (con _ _) (var _) = no (λ ()) 47 | _≟-Term_ (con _ _) (lit _) = no (λ ()) 48 | _≟-Term_ (con s₁ ts₁) (con s₂ ts₂) with s₁ ≟-Name s₂ 49 | ... | no s₁≠s₂ = no (s₁≠s₂ ∘ proj₁ ∘ con-inj) 50 | ... | yes s₁=s₂ rewrite s₁=s₂ with ts₁ ≟-Terms ts₂ 51 | ... | no ts₁≠ts₂ = no (ts₁≠ts₂ ∘ proj₂ ∘ con-inj) 52 | ... | yes ts₁=ts₂ = yes (cong (con s₂) ts₁=ts₂) 53 | _≟-Term_ (lit _) (var _) = no (λ ()) 54 | _≟-Term_ (lit _) (con _ _) = no (λ ()) 55 | _≟-Term_ (lit x₁) (lit x₂) with x₁ ≟-Literal x₂ 56 | ... | yes x₁=x₂ = yes (cong lit x₁=x₂) 57 | ... | no x₁≠x₂ = no (x₁≠x₂ ∘ lit-inj) 58 | 59 | _≟-Terms_ : ∀ {n} (xs ys : List (Term n)) → Dec (xs ≡ ys) 60 | _≟-Terms_ [] [] = yes refl 61 | _≟-Terms_ [] (_  ∷ _) = no (λ ()) 62 | _≟-Terms_ (_ ∷ _) [] = no (λ ()) 63 | _≟-Terms_ (x ∷ xs) (y ∷ ys) with x ≟-Term y 64 | ... | no x≠y = no (x≠y ∘ proj₁ ∘ ∷-inj) 65 | ... | yes x=y with xs ≟-Terms ys 66 | ... | no xs≠ys = no (xs≠ys ∘ proj₂ ∘ ∷-inj) 67 | ... | yes xs=ys = yes (cong₂ _∷_ x=y xs=ys) 68 | 69 | 70 | -- defining thick and thin 71 | thin : {n : ℕ} → Fin (suc n) → Fin n → Fin (suc n) 72 | thin zero y = suc y 73 | thin (suc x) zero = zero 74 | thin (suc x) (suc y) = suc (thin x y) 75 | 76 | thick : {n : ℕ} → (x y : Fin (suc n)) → Maybe (Fin n) 77 | thick zero zero = nothing 78 | thick zero (suc y) = just y 79 | thick {zero} (suc ()) _ 80 | thick {suc n} (suc x) zero = just zero 81 | thick {suc n} (suc x) (suc y) = suc <$> thick x y 82 | 83 | -- defining replacement function (written _◂ in McBride, 2003) 84 | replace : ∀ {n m} → (Fin n → Term m) → Term n → Term m 85 | replace _ (lit l) = lit l 86 | replace f (var i) = f i 87 | replace f (con s ts) = con s (replaceChildren f ts) 88 | where 89 | replaceChildren : ∀ {n m} → (Fin n → Term m) → (List (Term n) → List (Term m)) 90 | replaceChildren f [] = [] 91 | replaceChildren f (x ∷ xs) = replace f x ∷ (replaceChildren f xs) 92 | 93 | -- defining replacement composition 94 | _◇_ : ∀ {m n l} (f : Fin m → Term n) (g : Fin l → Term m) → Fin l → Term n 95 | _◇_ f g = replace f ∘ g 96 | 97 | -- defining an occurs check (**check** in McBride, 2003) 98 | check : ∀ {n} (x : Fin (suc n)) (t : Term (suc n)) → Maybe (Term n) 99 | check _ (lit l) = just (lit l) 100 | check x₁ (var x₂) = var <$> thick x₁ x₂ 101 | check x₁ (con s ts) = con s <$> checkChildren x₁ ts 102 | where 103 | checkChildren : ∀ {n} (x : Fin (suc n)) (ts : List (Term (suc n))) → Maybe (List (Term n)) 104 | checkChildren x₁ [] = just [] 105 | checkChildren x₁ (t ∷ ts) = check x₁ t >>= λ t' → 106 | checkChildren x₁ ts >>= λ ts' → return (t' ∷ ts') 107 | 108 | -- datatype for substitutions (AList in McBride, 2003) 109 | data Subst : ℕ → ℕ → Set where 110 | nil : ∀ {n} → Subst n n 111 | snoc : ∀ {m n} → (s : Subst m n) → (t : Term m) → (x : Fin (suc m)) → Subst (suc m) n 112 | 113 | -- substitutes t for x (**for** in McBride, 2003) 114 | _for_ : ∀ {n} (t : Term n) (x : Fin (suc n)) → Fin (suc n) → Term n 115 | _for_ t x y with thick x y 116 | _for_ t x y | just y' = var y' 117 | _for_ t x y | nothing = t 118 | 119 | -- substitution application (**sub** in McBride, 2003) 120 | sub : ∀ {m n} → Subst m n → Fin m → Term n 121 | sub nil = var 122 | sub (snoc s t x) = (sub s) ◇ (t for x) 123 | 124 | -- composes two substitutions 125 | _++_ : ∀ {l m n} → Subst m n → Subst l m → Subst l n 126 | s₁ ++ nil = s₁ 127 | s₁ ++ (snoc s₂ t x) = snoc (s₁ ++ s₂) t x 128 | 129 | flexRigid : ∀ {n} → Fin n → Term n → Maybe (∃ (Subst n)) 130 | flexRigid {zero} () t 131 | flexRigid {suc n} x t with check x t 132 | flexRigid {suc n} x t | nothing = nothing 133 | flexRigid {suc n} x t | just t' = just (n , snoc nil t' x) 134 | 135 | flexFlex : ∀ {n} → (x y : Fin n) → ∃ (Subst n) 136 | flexFlex {zero} () j 137 | flexFlex {suc n} x y with thick x y 138 | flexFlex {suc n} x y | nothing = (suc n , nil) 139 | flexFlex {suc n} x y | just z = (n , snoc nil (var z) x) 140 | 141 | mutual 142 | unifyAcc : ∀ {m} → (t₁ t₂ : Term m) → ∃ (Subst m) → Maybe (∃ (Subst m)) 143 | unifyAcc (lit x₁) (lit x₂) (n , nil) with x₁ ≟-Literal x₂ 144 | ... | yes x₁=x₂ rewrite x₁=x₂ = just (n , nil) 145 | ... | no x₁≠x₂ = nothing 146 | unifyAcc (con s ts) (lit x) (n , nil) = nothing 147 | unifyAcc (lit x) (con s ts) (n , nil) = nothing 148 | unifyAcc (con s₁ ts₁) (con s₂ ts₂) acc with s₁ ≟-Name s₂ 149 | ... | yes s₁=s₂ rewrite s₁=s₂ = unifyAccChildren ts₁ ts₂ acc 150 | ... | no s₁≠s₂ = nothing 151 | unifyAcc (var x₁) (var x₂) (n , nil) = just (flexFlex x₁ x₂) 152 | unifyAcc t (var x) (n , nil) = flexRigid x t 153 | unifyAcc (var x) t (n , nil) = flexRigid x t 154 | unifyAcc t₁ t₂ (n , snoc s t′ x) = 155 | ( λ s → proj₁ s , snoc (proj₂ s) t′ x ) 156 | <$> unifyAcc (replace (t′ for x) t₁) (replace (t′ for x) t₂) (n , s) 157 | 158 | unifyAccChildren : ∀ {n} → (ts₁ ts₂ : List (Term n)) → ∃ (Subst n) → Maybe (∃ (Subst n)) 159 | unifyAccChildren [] [] acc = just acc 160 | unifyAccChildren [] _ _ = nothing 161 | unifyAccChildren _ [] _ = nothing 162 | unifyAccChildren (t₁ ∷ ts₁) (t₂ ∷ ts₂) acc = unifyAcc t₁ t₂ acc >>= unifyAccChildren ts₁ ts₂ 163 | 164 | unify : ∀ {m} → (t₁ t₂ : Term m) → Maybe (∃ (Subst m)) 165 | unify {m} t₁ t₂ = unifyAcc t₁ t₂ (m , nil) 166 | --------------------------------------------------------------------------------