Code Generation for Higher Inductive Types

08/24/2018
by   Paventhan Vivekanandan, et al.
Indiana University
0

Higher inductive types are inductive types that include nontrivial higher-dimensional structure, represented as identifications that are not reflexivity. While work proceeds on type theories with a computational interpretation of univalence and higher inductive types, it is convenient to encode these structures in more traditional type theories with mature implementations. However, these encodings involve a great deal of error-prone additional syntax. We present a library that uses Agda's metaprogramming facilities to automate this process, allowing higher inductive types to be specified with minimal additional syntax.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 1

page 2

page 3

page 4

02/04/2018

On Higher Inductive Types in Cubical Type Theory

Cubical type theory provides a constructive justification to certain asp...
09/17/2018

Verification of High-Level Transformations with Inductive Refinement Types

High-level transformation languages like Rascal include expressive featu...
02/11/2022

Inference with System W Satisfies Syntax Splitting

In this paper, we investigate inductive inference with system W from con...
06/21/2018

Indexed type theories

In this paper, we define indexed type theories which are related to inde...
07/16/2021

Touring the MetaCoq Project (Invited Paper)

Proof assistants are getting more widespread use in research and industr...
09/11/2020

Internalizing Representation Independence with Univalence

In their usual form, representation independence metatheorems provide an...
02/19/2020

Constructing Higher Inductive Types as Groupoid Quotients

In this paper, we show that all finitary 1-truncated higher inductive ty...
This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1 Introduction

Type theory unites programming and mathematics in a delightful synthesis, in which we can write programs and proofs in the same language. Work on higher-dimensional type theory has revealed a beautiful higher-dimensional structure, lurking just beyond reach. In particular, higher inductive types provide a natural encoding of many otherwise-difficult mathematical concepts, and univalence lets us work in our type theory the way we do on paper: up to isomorphism. Homotopy type theory, however, is not yet done. We do not yet have a mature theory or a mature implementation.

While work proceeds on prototype implementations of higher-dimensional type theories [26][25], much work remains before they will be as convenient for experimentation with new ideas as Coq, Agda, or Idris is today. In the meantime, it is useful to be able to experiment with ideas from higher-dimensional type theory in our existing systems. If one is willing to put up with some boilerplate code, it is possible to encode higher inductive types and univalence using postulated identities.

Boilerplate postulates, however, are not just inconvenient, they are also an opportunity to make mistakes. Luckily, this boilerplate code can be mechanically generated using Agda’s recent support for elaborator reflection [6], a paradigm for metaprogramming in an implementation of type theory. An elaborator is the part of the implementation that translates a convenient language designed for humans into a much simpler, more explicit, verbose language designed to be easy for a machine to process. Elaborator reflection directly exposes the primitive components of the elaborator to metaprograms written in the language being elaborated, allowing them to put these components to new uses.

Homotopy type theory has thus far primarily been applied to the encoding of mathematics, rather than to programming. Nevertheless, there are a few applications of homotopy type theory to programming. Applications such as homotopical patch theory [2] discuss a model of the core of the of Darcs [7] version control system using patch theory [17] encoded as a HIT. Containers in homotopy type theory [12, 16] implement data structures such as multisets and cycles. Automating the HIT boilerplate code allows more programmers to begin experimenting with programming with HITs.

Using Agda’s elaborator reflection, we automatically generate the support code for many useful higher inductive types, specifically those that include additional paths between constructors, but not paths between paths, which is sufficient for treating various interesting examples on the programming side [2][21][32]. We automate the production of the recursion principles, induction principles, and their computational behavior. Angiuli et al.’s encoding of patch theory as a higher inductive type [2] requires approximately 1500 lines of code when represented using rewrite mechanism. Using our library, the encoding can be expressed in just 70 lines.

This paper makes the following contributions:

  • We describe the design and implementation of a metaprogram that automates an encoding of higher inductive types with one path dimension using Agda’s new metaprogramming system.

  • We demonstrate applications of this metaprogram to examples from the literature, including both standard textbook examples of higher inductive types as well as larger systems, including both patch theory and specifying cryptographic schemes.

  • This metaprogram serves as an example of the additional power available in Agda’s elaborator reflection relative to earlier metaprogramming APIs.

In Agda, we don’t have built-in primitives to support the definition of higher inductive types. In this paper, we use Agda’s rewrite rules mechanism to define higher inductive types [29][30]. Unlike [30], we use basic modules, without parameters, to encode higher inductive types. This is because Agda’s reflection library does not have primitives to support introducing parameterized modules.

2 Background

2.1 Higher Inductive Types

Homotopy type theory [1] is a research program that aims to develop univalent, higher-dimensional type theories. A universe is univalent when equivalences between types are considered equivalent to identifications between types. A type theory is univalent when every universe in the type theory is univalent; it is higher-dimensional when we allow non-trivial identifications that every structure in the theory must nevertheless respect. Identifications between elements of a type are considered to be at the lowest dimension, while identifications between identifications at dimension are at dimension . Voevodsky added univalence to type theories as an axiom, asserting new identifications without providing a means to compute with them. While more recent work arranges the computational mechanisms of the type theory such that univalence can be derived, as is done in cubical type theories [26][25], we are concerned with modeling concepts from homotopy type theory in existing, mature implementations of type theory, so we follow Univalent Foundations Program [1] in modeling paths using Martin-Löf’s identity type. Higher-dimensional structure can arise from univalence, but it can also be introduced by defining new type formers that introduce not only introduction and elimination principles, but also new non-trivial identifications.

In homotopy type theories, one tends to think of types not as collections of distinct elements, but rather through the metaphor of topological spaces. The individual elements of the type correspond with points in the topological space, and identifications correspond to paths in this space.

While work proceeds on the general schematic characterization of higher inductive types[10][32][8], it is convenient to syntactically represent the higher inductive types that we know are acceptable using a syntax similar to a traditional inductive type by providing its constructors (i.e. its points); we additionally specify the higher-dimensional structure by providing additional constructors for paths. For example, Figure 1 describes —Circle—, which is a higher inductive type with one point constructor —base— and one non-trivial path constructor —loop—.

data Circle : Set where base : Circle loop : base ≡ base

Figure 1: A specification of a higher inductive type

postulate _↦_: ∀ i A : Set i → A → A → Set i

-# BUILTIN REWRITE _↦_#-

module Circle where

postulate S : Set base : S loop : base ≡ base

postulate recS : S → (C : Set) → (cbase : C) → (cloop : cbase ≡ cbase) → C βbase : (C : Set) → (cbase : C) → (cloop : cbase ≡ cbase) → recS base C cbase cloop ↦ cbase

-# REWRITE βbase #-

postulate βloop : (C : Set) → (cbase : C) → (cloop : cbase ≡ cbase) → ap (λ x → recS x C cbase cloop) loop ≡ cloop

postulate indS : (x : S) → (C : S → Set) → (cbase : C base) → (cloop : transport C loop cbase ≡ cbase) → C x iβbase : (C : S → Set) → (cbase : C base) → (cloop : transport C loop cbase ≡ cbase) → indS base C cbase cloop ↦ cbase

-# REWRITE iβbase #-

postulate iβloop : (C : S → Set) → (cbase : C base) → (cloop : transport C loop cbase ≡ cbase) → apd (λ x → indS x C cbase cloop) loop ≡ cloop

Figure 2: A HIT encoded using rewrite rules

Figure 2 represents the implementation of Circle in Agda. Inside module Circle, the type S and the constructors base and loop and the recursion and induction principles are declared as postulates. recS ignores the path argument and simply computes to the appropriate answer for the point constructor. The computation rule for point base is declared as a rewrite rule using —-# REWRITE , …#-— pragma. The computation rule for the path constructor loop is postulated using reduction rule βloop. The operator ap is frequently referred to as cong, because it expresses that propositional equality is a congruence. However, when viewed through a homotopy type theory lens, it is often called ap, as it describes the action of a function on paths. In a higher inductive type, ap should compute new paths from old ones.

ap : A B : Set x y : A (f : A → B) (p : x ≡ y) → f x ≡ f y

In addition to describing the constructors of the points and paths of S, Figure 2 additionally demonstrates the dependent eliminator (that is, the induction rule) indS and its computational meaning. The dependent eliminator relies on another operation on identifications, called transport, that coerces an inhabitant of a family of types at a particular index into an inhabitant at another index. Outside of homotopy type theory, transport is typically called subst or replace, because it also expresses that substituting equal elements for equal elements is acceptable.

transport : A : Set x y : A → (P : A → Set) → (p : x ≡ y) → P x → P y

In the postulated computation rule for indS, the function apd is the dependent version of ap: it expresses the action of dependent functions on paths.

apd : A : Set B : A → Set x y : A → (f : (a : A) → B a) → (p : x ≡ y) → transport B p (f x) ≡ f y

2.2 Agda Reflection

Agda [27]

is a functional programming language with full dependent types and dependent pattern matching. Agda’s type theory has gained a number of new features over the years, among them the ability to restrict pattern matching to that subset that does not imply Streicher’s Axiom K 

[28], which is inconsistent with univalence. The convenience of programming in Agda, combined with the ability to avoid axiom K, makes it a good laboratory for experimenting with the idioms and techniques of univalent programming while more practical implementations of univalent type theories are under development.

Agda’s reflection library enables compile-time metaprogramming. This reflection library directly exposes parts of the implementation of Agda’s type checker and elaborator for use by metaprograms, in a manner that is similar to Idris’s elaborator reflection [22, 6] and Lean’s tactic metaprogramming [23]. The type checker’s implementation is exposed as effects in a monad called TC.

Agda exposes a representation of its syntax to metaprograms, including datatypes for expressions (called Term) and definitions (called Definition). The primitives exposed in TC include declaring new metavariables, unifying two Terms, declaring new definitions, adding new postulates, computing the normal form or weak head normal form of a Term, inspecting the current context, and constructing fresh names. This section describes the primitives that are used in our code generation library; more information on the reflection library can be found in the Agda documentation [5].

TC computations can be invoked in three ways: by macros, which work in expression positions, using the unquoteDecl operator in a declaration position, which can bring new names into scope, and using the unquoteDef operator in a declaration position, which can automate constructions using names that are already in scope. This preserves the principle in Agda’s design that the system never invents a name.

An Agda macro is a function of type → Term → TC ⊤ that is defined inside a macro block. Macros are special: their last argument is automatically supplied by the type checker and consists of a Term that represents the metavariable to be solved by the macro. If the remaining arguments are quoted names or Terms, then the type checker will automatically quote the arguments at the macro’s use site. At some point, the macro is expected to unify the provided metavariable with some other term, thus solving it.

macro mc1 : Term → Term → TC ⊤ mc1 exp hole = do exp’ ← quoteTC exp unify hole exp’ sampleTerm : Term sampleTerm = mc1 (λ (n : Nat) → n) Figure 3: A macro that quotes its argument macro mc2 : Term → Term → TC ⊤ mc2 exp hole = do exp’ ← unquoteTC exp unify hole exp’ sampleSyntax : Nat → Nat sampleSyntax = mc2 (lam visible (abs ”n” (var 0 [])))
Figure 4: A macro that unquotes its argument

Figure 3 demonstrates a macro that quotes its argument. The first step is to quote the quoted expression argument again, using quoteTC, yielding a quotation of a quotation. This double-quoted expression is passed, using Agda’s new support for Haskell-style do-notation, into a function that unifies it with the hole. Because unification removes one layer of quotation, unify inserts the original quoted term into the hole. The value of sampleTerm is

lam visible (abs ”n” (var 0 []))

The constructor lam represents a lambda, and its body is formed by the abstraction constructor abs that represents a scope in which a new name "n" is bound. The body of the abstraction is a reference back to the abstracted name using de Bruijn index 0.

The unquoteTC primitive removes one level of quotation. Figure 4 demonstrates the use of unquoteTC. The macro mc2 expects a quotation of a quotation and substitutes its unquotation for the current metavariable.

The unquoteDecl and unquoteDef primitives, which run TC computations in a declaration context, will typically introduce new declarations by side effect. A function of a given type is declared using declareDef, and it can be given a definition using defineFun. Similarly, a postulate of a given type is defined using declarePostulate. Figure 5 shows an Agda implementation of addition on natural numbers, while Figure 6 demonstrates an equivalent metaprogram that adds the same definition to the context.

plus : Nat → Nat → Nat plus zero b = b plus (suc n) b = suc (plus n b)

Figure 5: Addition on natural numbers

pattern vArg x = arg (arg-info visible relevant) x pattern _‘⇒_a b = pi (vArg a) (abs ”_” b) pattern ‘Nat = def (quote Nat) []

unquoteDecl plus = do declareDef (vArg plus) (‘Nat ‘⇒ ‘Nat ‘⇒ ‘Nat) defineFun plus (clause (vArg (con (quote zero) []) :: vArg (var ”y”) :: []) (var 0 []) :: clause (vArg (con (quote suc) (vArg (var ”x”) :: [])) :: vArg (var ”y”) :: []) (con (quote suc) (vArg (def plus (vArg (var 1 []) :: vArg (var 0 []) :: [])) :: [])) :: [])

Figure 6: Addition, defined by metaprogramming

In Figure 6, declareDef declares the type of plus. The constructor pi represents dependent function types, but a pattern synonym is used to make it shorter. Similarly, def constructs references to defined names, and the pattern synonym —‘Nat— abbreviates references to the defined name Nat, and vArg represents the desired visibility and relevance settings of the arguments. Once declared, plus is defined using defineFun, which takes a name and a list of clauses, defining the function by side effect. Each clause consists of a pattern and a right-hand side. Patterns have their own datatype, while right-hand sides are Terms. The name con is overloaded: in patterns, it denotes a pattern that matches a particular constructor, while in Terms, it denotes a reference to a constructor.

The next section introduces the necessary automation features by describing the automatic generation of eliminators for a variant on Dybjer’s inductive families. Section 4 then generalizes this feature to automate the production of eliminators for higher inductive types using the rewrite mechanism. Section 5 revisits Angiuli et al.’s encoding of Darcs’s patch theory [2] and demonstrates that the higher inductive types employed in that paper can be generated succinctly using our library111Please see https://github.com/pavenvivek/WFLP-18.

3 Code Generation for Inductive Types

An inductive type is a type that is freely generated by a finite collection of constructors. The constructors of accept zero or more arguments and have as the co-domain. The constructors can also take an element of type itself as an argument, but only strictly positively: any occurrences of the type constructor in the type of an argument to a constructor of must not be to the left of any arrows. Type constructors can have a number of parameters, which may not vary between the constructors, as well as indices, which may vary.

In Agda, constructors are given a function type. In Agda’s reflection library, the constructor data-type of the datatype Definition stores the constructors of an inductive type as a list of Names. The type of a constructor can be retrieved by giving its Name as an input to the getType primitive. In this section, we discuss how to use the list of constructors and their types to generate code for the elimination rules of an inductive type.

3.1 Non-dependent Eliminators

In Agda, we define an inductive type using data keyword. A definition of an inductive datatype declares its type and specifies its constructors. While Agda supports a variety of ways to define new data types, we will restrict our attention to the subset that corresponds closely to Dyber’s inductive families. In general, the definition of an inductive datatype with constructors has the following form:

where the index instantiations are expressions in the scope induced by the telescope . Every expression in the definition must also be well-typed according to the provided declarations. A telescope is a sequence of types where later types may depend on elements of previous types.

data Vec (A : Set) : Nat → Set where [] : Vec A zero _::_: n : Nat → (x : A) → (xs : Vec A n) → Vec A (suc n)

Figure 7: Length-indexed lists

As an example, the datatype Vec (Figure 7) represents lists of a known length. There is one parameter, namely (A : Set), and one index, namely Nat. The second constructor, _::_, has a recursive instance of Vec as an argument.

While inductive datatypes are essentially characterized by their constructors, it must also be possible to eliminate their inhabitants, exposing the information in the constructors. This section describes an Agda metaprogram that generates a non-dependent recursion principle for an inductive type; section 3.2 generalizes this technique to fully dependent induction principles.

For Vec, the recursion principle says that, in order to eliminate a Vec A n, one must provide a result for the empty Vec and a means for transforming the head and tail of a non-empty Vec combined with the result of recursion onto a tail into the desired answer for the entire Vec. Concretely, the type of the recursor recVec is given as follows.

recVec : (A : Set) → n : Nat → Vec A n → (C : Set) → (base : C) → (step : n : Nat → (x : A) → (xs : Vec A n) → C → C) → C

The recursor recVec maps the constructor [], which takes zero arguments, to base. It maps (x :: xs) to (step x xs (recVec xs C base step)). Because step is applied to a recursive call to the recursor, it takes one more argument than the constructor _::_.

Based on the schematic presentation of inductive types earlier in this section, we can define a schematic representation for their non-dependent eliminators .

The type of , which is the method for fulfilling the desired type when eliminating the constructor , is determined by the type of . The telescope is the same as for non-recursive constructor arguments. However, binds additional variables when there are recursive occurrences of in the arguments. For instance, if has an argument , where is not an application of or a function returning such an application, binds directly. If is an application of , then an additional binding is inserted following . Finally, if is a function type , the additional binding is .

To construct the type of recVec, we need to build the types of base and step. These are derived from the corresponding types of [] and _::_, which can be discovered using reflection primitives. Since [] requires no arguments, its corresponding method is (base : C). The constructor pi of type Term encodes the abstract syntax tree (AST) representation of _::_. We can retrieve and traverse the AST of _::_, and add new type information into it to build a new type representing step. Once the AST for step’s type has been found, it is possible to build the type of recVec. To quantify over the return type (C : Set), we use the Term constructor agda-sort to refer to Set.

In general, when automating the production of , all the information that is needed to produce the type signature is available in the TC monad by looking up ’s definition. The constructor data-type contains the number of parameters occurring in a defined type. It also encodes the constructors of the type as a list of Names. Metaprograms can retrieve the index count by using the type and the number of parameters. The constructors of refer to the parameter and the index using de Bruijn indices.

The general schema for the computation rules corresponding to and constructors is as follows:

Here, is the sequence of variables bound in . constructs the application of the method to the arguments of , such that is satisfied. It is defined by recursion on . is , because all arguments have been accounted for. is when does not mention . is , where the recursive use of is applied to the recursive constructor argument as well as the appropriate indices, and the parameters, result type, and methods remain constant. Higher-order recursive arguments are a generalization of first-order arguments. Finally, is where the recursive use of is as before.

After declaring recVec’s type using declareDef, it is time to define its computational meaning using the schematic rules defined above. The computation rule representing the action of function recVec on [] and _::_ is defined using clause. The first argument to clause encodes variables corresponding to the above type, and it also includes the abstract representation of the constructors [] and _::_ on which the pattern matching should occur. The second argument to clause, which is of type Term, refers to the variables in the first argument using de Bruijn indices, and it encodes the output of recVec when the pattern matches. The computation rules for recVec are given as follows.

recVec [] C base step = base recVec (x :: xs) C base step = step x xs (f xs C base step)

generateRec (Figure 8) build the computation and elimination rules respectively. The recursion rule generated by generateRec is brought into scope using unquoteDecl. The first argument to generateRec is the quoted Name of the recursor encoded inside Arg, and the second argument is the quoted Name of the inductive type.

generateRec, generateInd : Arg Name → (indType : Name) → TC ⊤

generateβRec, generateβInd : Arg Name → List (Arg Name) → (indType : Name) → (param : Nat) → (points : List Name) → TC ⊤

generateRecHit, generateIndHit : Arg Name → (indType : Name) → (baseElim : Name) → (param : Nat) → (points : List Name) → (paths : List Name) → TC ⊤

generateβRecHitPath, generateβIndHitPath : Name → List (Arg Name) → (indType : Name) → (baseElim : Name) → (param : Nat) → (points : List Name) → (paths : List Name) → TC ⊤

Figure 8: Library for generating dependent and non-dependent eliminators

3.2 Dependent Eliminators

The dependent eliminator for a datatype, also known as the induction principle, is used to eliminate elements of a datatype when the type resulting from the elimination mentions the very element being eliminated. The type of the induction principle for is:

Unlike the non-dependent recursion principle , the result type is now computed from the target and its indices. Because it expresses the reason that the target must be eliminated, the function is often referred to as the motive. Similarly to , the type of each method is derived from the type of the constructor —the method argument telescope is similar, except the arguments that represents the result of recursion now apply the motive to appropriate arguments. If has an argument , where is not an application of or a function returning such an application, still binds directly. If is an application of to parameters and indices , then an additional binding is inserted following . Finally, if is a function type , the additional binding is .

Following these rules, the induction principle for Vec can be defined as follows.

indVec : (A : Set) → n : Nat → (xs : Vec A n) → (C : n : Nat → Vec A n → Set) → (base : C []) → (step : n : Nat → (x : A) → (xs : Vec A n) → C xs → C (x :: xs)) → C xs

Automating the production of the dependent eliminator is an extension of the procedure for automating the production of the non-dependent eliminator. The computation rules for the induction principle are automated using the same approach as for the recursion principle. The generation of induction principles is carried out using generateInd (Figure 8).

4 Code Generation for Higher Inductive Types

In Agda, there are no built-in primitives to support the definition of higher inductive types. However, we can still define a higher inductive type using rewrite rules, as described in section 2.1. In this section, we discuss the automation of code generation for the elimination and the computation rules of higher inductive types. While the general formulation of higher inductive types is a subject of active research [8][9][11], we stick to a schema that follows the pattern of Basold et al.’s [32] general rules for higher inductive types.

4.1 Non-dependent Eliminators for HITs

The recursion principle of a higher inductive type maps the points and paths of to points and paths in an output type . We extend the general schema of the recursion principle given in section 3.1 by adding methods for path constructors (Figure 9).

Figure 9: Generic schema for recursor
Figure 10: Generic schema for computation rule corresponding to Grec

The schematic definition of supports only one-dimensional paths. The type of the method for a point constructor in is built the same way as for the normal inductive type , as described in section 3.1. The code generator builds the type of , method for path constructor in , by traversing the AST of . The arguments of are handled the same way as for the point constructor’s method . During the traversal, the code generator uses the base type recursor to map the point constructors of in the codomain of to . Determining the computation rules corresponding to points is similar to the computation rules corresponding to constructors of the inductive type , except that there are additional methods to handle paths. Paths compute new paths; the computation rules that govern the interaction of recursors and paths are named and postulated. They identify the action of the recursor on the path with the corresponding method. The computation rules corresponding to paths are postulated as given in Figure 10.

As an example, if the code for the circle HIT from section 2.1 has been generated, and the type is called S, then the recursor needs a method for base and one for loop. The method for base should be an inhabitant of C. If it is called cbase, then the method for loop should be a path cbase ≡ cbase. The types of the path methods depend on the values of the point methods. The code generator builds the type of loop’s method by traversing the AST of loop’s type, replacing references to point constructors with the result of applying the base type’s recursor to the point methods. The recursion rule recS follows this pattern.

recS : S → (C : Set) → (cbase : C) → (cloop : cbase ≡ cbase) → C

The code generator builds the computation rule for the point constructor base using the same approach as described in section 3.1 as if it were for the base type. Additionally, it includes variables in the clause definition for the path constructor loop. The code generator postulates the following computation rule βloop for the path constructor loop:

βloop : (C : Set) → (cbase : C) → (cloop : cbase ≡ cbase) → ap (λ x → recS x C cbase cloop) loop ≡ cloop

The application of function recS to the path loop substitutes the point base for the argument x and it evaluates to the path cloop in the output type C. In the tool, generateRecHit is used to build the elimination rule and the computation rules for points, and generateβRecHitPath is used to build the computation rules for paths (Figure 8). The third argument to generateRecHit is the base type’s recursor built using generateβRec that constructs the computation rules for points using rewrite rules. The parameter count is passed as the fourth argument.

4.2 Dependent Eliminators for HITs

The dependent eliminator for a higher inductive type is a dependent function that maps an element of to an output type . The general schema for the induction principle of is given in Figure 11.

Figure 11: Generic schema for induction principle
Figure 12: Generic schema for computation rule corresponding to Gind

Similar to , the type of is built the same way as for the normal inductive type . The code generator builds the type of the method for path constructor , called , in , by traversing the AST of . During the traversal, the code generator uses the base eliminator to map the point constructors of in the codomain of to . In the first argument to the identity type in the codomain of , the code generator adds an application of transport to the motive C and the path . The arguments of are handled the same way as for . The computation rules corresponding to paths are postulated as given in Figure 12.

For the type S with point constructor base and path constructor loop, to define a mapping indS : (x : S) → C x, we need cbase : C base and cloop : transport C loop cbase ≡ cbase, where cloop is a heterogeneous path transported over loop. The code generator builds the type of cloop by adding relevant type information to the type of loop. The type of the method for the path constructor cloop is derived by inserting a call to transport with arguments C, loop, and cbase. The code generator applies the base eliminator to map the point base to cbase during the construction of the codomain of cloop. The following declaration gives the type of indS.

indS : (circle : S) → (C : S → Set) → (cbase : C base) → (cloop : transport C loop cbase ≡ cbase) → C circle

The computation rule for base, which defines the action of indS on base, is built using the same approach as for the non-dependent eliminator recS. The postulated computation rule iβloop for the path loop uses apd which gives the action of dependent function indS on the path loop.

iβloop : (C : S → Set) → (cbase : C base) → (cloop : transport C loop cbase ≡ cbase) → apd (λ x → indS x C cbase cloop) loop ≡ cloop

generateIndHit is used to build the elimination rule and the computation rules for points, and generateβIndHitPath is used to build the computation rules for paths (Figure 8).

5 Applications

5.1 Patch Theory Revisited

We reimplemented Angiuli et al.’s patch theory [2] using our code generator in Agda. We implemented basic patches such as the insertion of a string as line in a file and deletion of a line from a file. The functions implementing insertion and deletion in the universe are not bijective. So, we used Angiuli et al.’s patch history approach to encode non-bijective functions. According to this approach, we developed a separate higher inductive type History which serves as the types of patches. We also implemented patches involving encryption or decryption with cryptosystems like RSA and Paillier. In addition to easing the implementation difficulties of higher inductive types, the code generator greatly reduced the code size. The type definitions shrank from around 1500 to around 70 lines, resulting in a 60% decrease in the overall number of lines of code in the development.

5.2 Cryptographic Protocols

Vivekanandan [21] models certain cryptographic protocols using homotopy type theory, introducing a new approach to formally specifying cryptographic schemes using types. The work discusses modeling cryptDB [3] using a framework similar to Angiuli et al.’s patch theory. CryptDB employs layered encryption techniques and homomorphic encryption. We can implement cryptDB by modeling the database queries as paths in a higher inductive type and mapping the paths to the universe using singleton types [2]. The code generator can be applied to generate code for the higher inductive type representing cryptDB and its corresponding elimination and computation rules. By using the code generator, we can decrease the length and increase the readability of the definitions, hopefully making it more accessible to the broad cryptographic community.

6 Related Work

Kokke and Swierstra [4] implemented a library for proof search using Agda’s old reflection primitives, from before it had elaborator reflection. They describe a Prolog interpreter in the style of Stutterheim et al. [18]. It employs a hint database and a customizable depth-first traversal, with lemmas to assist in the proof search.

Van der Walt and Swierstra [19] and van der Walt [20] discuss automating specific categories of proofs using proof by reflection. A key component of this proof technique is a means for converting an expression into a quoted representation. They automate this process, giving a user-defined datatype. Van der Walt [20] also give an overview of Agda’s old metaprogramming tools.

Datatype-generic programming [13][14][15] via universes allows defining a single function over an entire class of datatypes at once, saving developers the effort of implementing the operation for datatypes specific to their programs. As Agda’s reflection library evolves and the internal representation of datatypes changes, the tool described in this paper requires maintenance work. A future direction would be to work with a universe extended with support for higher inductive types. In such case, the only metaprograms necessary are those that convert to and from the universe. The metaprograms automating the elimination rules do not need to change as long as the universe is kept the same.

Ongoing work on cubical type theories [26][25][24] provides a computational interpretation of univalence and HITs. We strenuously hope that these systems quickly reach maturity, rendering our code generator obsolete. In the meantime, however, these systems are not yet as mature as Agda.

7 Conclusion and Future Work

We presented a code generator that generates the encodings of higher inductive types, developed using Agda’s new support for Idris-style elaborator reflection. In particular, the tool generates the dependent and non-dependent elimination rules and the computational rules for 1-dimensional higher inductive types. This syntax is greatly simplified with respect to writing the encoding by hand. We demonstrated an extensive reduction in code size by employing our tool. Next, we intend to extend the tool to support higher-dimensional paths in the definition of HITs, bringing its benefits to a wider class of problems.

Acknowledgements

The author is greatly indebted to David Christiansen for his contributions and advice, and the anonymous reviewers for their valuable review comments.

References

  • [1] The Univalent Foundations Program, Institute for Advanced Study. Homotopy Type Theory: Univalent Foundations Of Mathematics (2013). homotopytypetheory.org
  • [2] Anguili, C., Morehouse, E., Licata, D., Harper, R.: Homotopical Patch Theory. In: International Conference on Functional Programming (ICFP), Sweden (2014)
  • [3] Popa, R.A., Redfield, C.M.S, Zeldovich, N., Hari Balakrishnan, H. : CryptDB: Protecting Confidentiality with Encrypted Query Processing. In: Proceedings of the 23rd ACM Symposium on Operating Systems Principles (SOSP), Portugal (2011)
  • [4] Kokke, P., Swierstra, W.: Auto in Agda. In: Hinze R., Voigtländer J. Mathematics of Program Construction. Lecture Notes in Computer Science, vol 9129, Cham (2015).
  • [5] Agda’s Documentation (2017). https://agda.readthedocs.io/en/latest/
  • [6] Christiansen, D. and Brady, E.: Elaborator Reflection: Extending Idris in Idris. In: Proceedings of the 21st ACM SIGPLAN International Conference on Functional Programming (ICFP ’16). Nara, Japan (2016).
  • [7] Roundy, D.: Darcs: Distributed version management in haskell. ACM SIGPLAN Workshop on Haskell (2005).
  • [8] Dybjer, P., Moeneclaey, H.: Finitary higher inductive types in the groupoid model. In: Mathematical Foundations of Programming Semantics, 33rd International Conference. Ljubljana, Slovenia (2017).
  • [9] Lumsdaine, P.L., Shulman, M.: Semantics of higher inductive types. arXiv:1705.07088 (May 2017).
  • [10] Cavallo, E., Harper, R.: Computational Higher Type Theory IV: Inductive Types. arXiv:1801.01568 (July 2018)
  • [11] Kaposi, A., Kov´acs, A.: A syntax for higher inductive-inductive types. In 3nd International Conference on Formal Structures for Computation and Deduction (FSCD ’18). Oxford, UK (July 2018).
  • [12] Altenkirch, T.: Containers in homotopy type theory. Talk at Mathematical Structures of Computation, Lyon (2014).
  • [13] Altenkirch T., McBride C., Morris P.: Generic Programming with Dependent Types. In: Datatype-Generic Programming. SSDGP 2006. Lecture Notes in Computer Science, vol 4719. Springer, Berlin, Heidelberg
  • [14] Löh, A., Magalhães, J. P.: Generic Programming with Indexed Functors. WGP’11 - Proceedings of the 2011 ACM SIGPLAN Workshop on Generic Programming. 10.1145/2036918.2036920.
  • [15] Chapman, J., Dagand, P., McBride, C., Morris, P.: The gentle art of levitation. In: Proceedings of the 15th ACM SIGPLAN international conference on Functional programming, September, 2010, Baltimore, Maryland, USA.
  • [16] Abbott M., Altenkirch, T. and Ghani, N.: Containers: constructing strictly positive types. Theoretic Computer Science (2005).
  • [17] Mimram, S. and Giusto D. C.: A categorical theory of patches. Electronic Notes in Theoretic Computer Science, 298:283–307 (2013).
  • [18] Stutterheim, J., Swierstra, W. and Swierstra, D.: Forty hours of declarative programming: Teaching Prolog at the Junior College Utrecht. Electronic Proceedings in Theoretical Computer Science, volume 106, pages 50–62, 2013.
  • [19] van der Walt, P. and Swierstra, W.: Engineering proof by reflection in Agda. In: In Ralf Hinze, (ed) Implementation and Application of Functional Languages. Lecture Notes in Computer Science, pages 157–173. Springer Berlin Heidelberg, 2013.
  • [20] van der Walt, P.: Reflection in Agda. Master’s thesis, Department of Computer Science, Utrecht University, Utrecht, Netherlands (2012).
  • [21] Vivekanandan, P.: A Homotopical Approach to Cryptography. Workshop on Foundations of Computer Security (FCS’18), University of Oxford, UK (2018).
  • [22] Christiansen, D.: Practical Reflection and Metaprogramming for Dependent Types. IT University of Copenhagen (2016).
  • [23] Ebner, G., Ullrich, S., Roesch, J., Avigad, J., and de Moura, L.: A Metaprogramming Framework for Formal Verification. Proceedings of the ACM on Programming Languages. ICFP New York, NY, USA (2017).
  • [24] Coquand, T., Huber, S., and Mörtberg, A.: On Higher Inductive Types in Cubical Type Theory. arXiv:1802.01170 (2018).
  • [25] Angiuli, C., Harper, R., and Wilson, T.: Computational Higher-dimensional Type Theory. Proceedings of the 44th ACM SIGPLAN Symposium on Principles of Programming Languages (POPL’17), Paris, France (2017).
  • [26] Cohen, C., Coquand, T., Huber, S., and Mörtberg, A.: Cubical Type Theory: a constructive interpretation of the univalence axiom. 21st International Conference on Types for Proofs and Programs (2015).
  • [27] Norell, U.: Towards a practical programming language based on dependent type theory. Chalmers University of Technology, Sweden (2007).
  • [28] Cockx, J., Devriese, D., and Piessens, F.: Pattern Matching Without K. Proceedings of the 19th ACM SIGPLAN International Conference on Functional Programming (ICFP’14), Gothenburg, Sweden (2014).
  • [29] Cockx, J. and Abel, A.: Sprinkles of Extensionality for Your Vanilla Type Theory. 22nd International Conference on Types for Proofs and Programs (TYPES 2016).
  • [30] Brunerie, G.: Custom definitional equalities in Agda (Talk) in the “Univalent Foundations and Proof Assistants” session of the International Congress on Mathematical Software (ICMS 2016), Berlin, Germany, July 2016.
  • [31] Dybjer, P.: Inductive families. Formal Aspects of Computing (1994). https://doi.org/10.1007/BF01211308
  • [32] Basold, H., Geuvers, H. and van der Weide, N.: Higher Inductive Types in Programming. Journal of Universal Computer Science, vol. 23, no. 1 (2017).