1 Introduction

Mathematical structures are a key ingredient of modern formalized mathematics in proof assistants, e.g., [1, 18, 25, 41] [10, Chap. 2 and Chap. 4] [20, Sect. 3] [30, Chap. 5] [46, Sect. 4]. Since mathematical structures have an inheritance/subtyping hierarchy such that “a ring is a group and a group is a monoid”, it is usual practice in mathematics to reuse notations and theories of superclasses implicitly to reason about a subclass. Similarly, the sharing of notations and theories across the hierarchy is important for productivity when formalizing mathematics.

Fig. 1.
figure 1

The hierarchy of structures in the MathComp library 1.10.0

The packed classes methodology [16, 17] is a generic design pattern to define and combine mathematical structures in a dependent type theory with records. Hierarchies using packed classes support multiple inheritance, and maximal sharing notations and theories. When combined with mechanisms for implicit coercions [32, 33] and for extending unification procedure, such as the canonical structures [26, 33] of the Coq proof assistant [42], and the unification hints [4] of the Lean theorem prover [6, 27] and the Matita interactive theorem prover [5], packed classes enable subtyping and automated inference of structures in hierarchies. Compared to approaches based on type classes [22, 40], packed classes are more robust, and their inference approach is efficient and predictable [1]. The success of the packed classes methodology in formalized mathematics can be seen in the Mathematical Components library [45] (hereafter MathComp), the Coquelicot library [8], and especially the formal proof of the Odd Order Theorem [20]. It has also been successfully applied for program verification tasks, e.g., a hierarchy of monadic effects [2] and a hierarchy of partial commutative monoids [28] for Fine-grained Concurrent Separation Logic [39].

In spite of its success, the packed classes methodology is hard to master for library designers and requires a substantial amount of work to maintain as libraries evolve. For instance, the strict application of packed classes requires defining quadratically many implicit coercions and unification hints in the number of structures. To give some figures, the MathComp library 1.10.0 uses this methodology ubiquitously to define the 51 mathematical structures depicted in Fig. 1, and declares 554 implicit coercions and 746 unification hints to implement their inheritance. Moreover, defining new intermediate structures between existing ones requires fixing their subclasses and their inheritance accordingly; thus, it can be a challenging task.

In this paper, we indentify two hierarchy invariants concerning implicit coercions and unification hints in packed classes, and propose algorithms to check these invariants. We implement our algorithms as tools for the Coq system, evaluate our tools on a large-scale development, the MathComp library 1.7.0, and then successfully detect and fix several inheritance bugs with the help of our tools. The invariant concerning implicit coercions ensures the modularity of reasoning with packed classes and is also useful in other approaches, such as type classes and telescopes [26, Sect. 2.3], in a dependent type theory. This invariant was proposed before as a coherence of inheritance graphs [7]. The invariant concerning unification hints, that we call well-formedness, ensures the predictability of structure inference. Our tool not only checks well-formedness, but also generates an exhaustive set of assertions for structure inference, and these assertions can be tested inside Coq. We state the predictability of inference as a metatheorem on a simplified model of hierarchies, that we formally prove in Coq.

The paper is organized as follows: Sect. 2 reviews the packed classes methodology using a running example. Section 3 studies the implicit coercion mechanism of Coq, and then presents the new coherence checking algorithm and its implementation. Section 4 reviews the use of canonical structures for structure inference in packed classes, and introduces the notion of well-formedness. Section 5 defines a simplified model of hierarchies and structure inference, and shows the metatheorem that states the predictability of structure inference. Section 6 presents the well-formedness checking algorithm and its implementation. Section 7 evaluates our checking tools on the MathComp library 1.7.0. Section 8 discusses related work and concludes the paper. Our running example for Sect. 2, Sect. 4, and Sect. 6, the formalization for Sect. 5, and the evaluation script for Sect. 7 are available at [37].

2 Packed Classes

This section reviews the packed classes methodology [16, 17] through an example, but elides canonical structures. Our example is a minimal hierarchy with multiple inheritance, consisting of the following four algebraic structures (Fig. 2):

  • Additive monoids \((A, +, 0)\): Monoids have an associative binary operation \(+\) on the set A and an identity element \(0 \in A\).

  • Semirings \((A, +, 0, \times , 1)\): Semirings have the monoid axioms, commutativity of addition, multiplication, and an element \(1 \in A\). Multiplication \(\times \) is an associative binary operation on A that is left and right distributive over addition. 0 and 1 are absorbing and identity elements with respect to multiplication, respectively.

  • Additive groups \((A, +, 0, -)\): Groups have the monoid axioms and a unary operation − on A. \(- x\) is the additive inverse of x for any \(x \in A\).

  • Rings \((A, +, 0, -, \times , 1)\): Rings have all the semiring and group axioms, but no additional axioms.

Fig. 2.
figure 2

Hierarchy diagram for monoids, semirings, groups, and rings, where an arrow from \(\texttt {X.type}\) to \(\texttt {Y.type}\) means that directly inherits from . The monoid structure is the superclass of all other structures. Semirings and groups directly inherit from monoids. Rings directly inherit from semirings and groups, and indirectly inherit from monoids.

We start by defining the base class, namely, the structure.

figure d

The above definitions are enclosed by the module, which forces users to write qualified names such as . Thus, we can reuse the same name of record types ( , , and ), their constructors ( , , and ), and constants (e.g., and ) for other structures to indicate their roles. Structures are written as records that have three different roles: mixins, classes, and structures. The mixin record (line 3) gathers operators and axioms newly introduced by the structure. Since monoids do not inherit any other structure in Fig. 2, those are all the monoid operators, namely 0 and \(+\), and their axioms. The class record (line 11) assembles all the mixins of the superclasses of the structure (including itself), which is the singleton record consisting of the monoid mixin. The structure record (line 13) is the actual interface of the structure that bundles a carrier of type and its class instance. and are synonyms in Coq, but we reserve the latter for actual interfaces of structures. In a hierarchy of algebraic structures, a carrier set has type ; hence, for each structure, the first field of the record should have type , and the record should be parameterized by that carrier. In general, it can be other types, e.g., in the hierarchy of functors and monads [2], but should be fixed in each hierarchy of structures.

Mixin and monoid records are internal definitions of mathematical structures; in contrast, the structure record is a part of the interface of the monoid structure when reasoning about monoids. For this reason, we lift the projections for to definitions and lemmas for as follows.

figure ab

The curly brackets enclosing mark it as an implicit argument; in contrast, is the explicit application symbol that deactivates the hiding of implicit arguments.

Since a monoid instance can be seen as a type equipped with monoid axioms, it is natural to declare as an implicit coercion. The types of can be written and shown as rather than thanks to this implicit coercion.

figure aj

Next, we define the structure. Since semirings inherit from monoids and the semiring axioms interact with the monoid operators, e.g., distributivity of multiplication over addition, the semiring mixin should take rather than as its argument.

figure an

The class packs the mixin together with the class to assemble the mixin records of monoids and semirings. We may also assemble all the required mixins as record fields directly rather than nesting class records, yielding what is called the flat variant of packed classes [14, Sect. 4]. Since the semiring mixin requires as its type argument, we have to bundle the monoid class with the carrier to provide that instance, as follows.

figure at

The inheritance from monoids to semirings can then be expressed as a canonical way to construct a monoid from a semiring as below.

figure au

Following the above method, we declare as an implicit coercion, and then lift , , and the semiring axioms from projections for the mixin to definitions for .

figure az

In the statement of the axiom (line 6 just above), we need to explicitly write to get the canonical instance for . We omit this subtyping function by declaring it as an implicit coercion. In general, for a structure inheriting from other structures, we define implicit coercions from to all its superclasses.

figure bh

The structure is monoids extended with an additive inverse. Following the above method, it can be defined as follows.

figure bj

The structure can be seen both as groups extended by the semiring axioms and as semirings extended by the group axioms. Here, we define it in the first way, but one may also define it in the second way. Since rings have no other axioms than the group and semiring axioms, no additional record is needed.Footnote 1

figure bn

The ring structure inherits from monoids, groups, and semirings. Here, we define implicit coercions from the ring structure to those superclasses.

figure bo

3 Coherence of Implicit Coercions

This section describes the implicit coercion mechanism of Coq and the coherence property [7] of inheritance graphs that ensures modularity of reasoning with packed classes, and presents the coherence checking mechanism we implemented in Coq. More details on implicit coercions can be found in the Coq reference manual [44], and its typing algorithm is described in [32]. First, we define classes and implicit coercions.

Definition 3.1

(Classes [32, Sect. 3.1] [44]). A class with n parameters is a defined name C with a type \(\forall (x_1 : T_1) \dots (x_n : T_n), sort \) where \( sort \) is , , , or . Thus, a class with parameters is considered a single class and not a family of classes. An object of class C is any term of type \(C \, t_1 \dots t_n\).

Definition 3.2

(Implicit coercions). A name f can be declared as an implicit coercion from a source class C to a target class D with k parameters if the type of f has the form \(\forall x_1 \dots x_k \, (y : C \, t_1 \dots t_n), D \, u_1 \dots u_m\). We then write \(f : C \rightarrowtail D\).Footnote 2

An implicit coercion \(f : C \rightarrowtail D\) can be seen as a subtyping \(C \le D\) and applied to fill type mismatches to a term of class C placed in a context that expects to have a term of class D. Implicit coercions form an inheritance graph with classes as nodes and coercions as edges, whose path \([f_1; \dots ; f_n]\) where \(f_i : C_i \rightarrowtail C_{i + 1}\) can also be seen as a subtyping \(C_1 \le C_{n + 1}\); thus, we write \([f_1; \dots ; f_n] : C_1 \rightarrowtail C_{n + 1}\) to indicate \([f_1; \dots ; f_n]\) is an inheritance path from \(C_1\) to \(C_{n + 1}\). The Coq system pre-computes those inheritance paths for any pair of source and target classes, and updates to keep them closed under transitivity when a new implicit coercion is declared [32, Sect. 3.3] [44, Sect. 8.2.5 “Inheritance Graph”]. The coherence of inheritance graphs is defined as follows.

Definition 3.3

(Definitional equality [43] [15, Sect. 3.1]). Two terms \(t_1\) and \(t_2\) are said to be definitionally equal, or convertible, if they are equivalent under \(\beta \delta \iota \zeta \)-reduction and \(\eta \)-expansion. This equality is denoted by the infix symbol \(\equiv \).

Definition 3.4

(Coherence [7, Sect. 3.2] [32, Sect. 7]). An inheritance graph is coherent if and only if the following two conditions hold.

  1. 1.

    For any circular inheritance path \(p : C \rightarrowtail C\), \(p \, x \equiv x\), where x is a fresh variable of class C.

  2. 2.

    For any two inheritance paths \(p, q : C \rightarrowtail D\), \(p \, x \equiv q \, x\), where x is a fresh variable of class C.

Before our work, if multiple inheritance paths existed between the same source and target class, only the oldest one was kept as a valid one in the inheritance graph in Coq, and all the others were reported as ambiguous paths and ignored. We improved this mechanism to report only paths that break the coherence conditions and also to minimize the number of reported ambiguous paths [34, 35]. The second condition ensures the modularity of reasoning with packed classes. For example, proving requires using both and implicitly. If those instances are not definitionally equal, it will prevent us from proving the lemma by reporting type mismatch between and .

Convertibility checking for inheritance paths consisting of implicit coercions as in Definition 3.2 requires constructing a composition of functions for a given inheritance path. One can reduce any unification problem to this well-typed term construction problem, that in the higher-order case is undecidable [19]. However, the inheritance paths that make the convertibility checking undecidable can never be applied as implicit coercions in type inference, because they do not respect the uniform inheritance condition.

Definition 3.5

(Uniform inheritance condition [44] [32, Sect. 3.2]). An implicit coercion f between classes \(C \rightarrowtail D\) with n and m parameters, respectively, is uniform if and only if the type of f has the form

$$ \forall (x_1 : A_1) \dots (x_n : A_n) \, (x_{n + 1} : C \, x_1 \dots x_n), D \, u_1 \dots u_m. $$

Remark 3.1

Names that can be declared as implicit coercions are defined as constants that respect the uniform inheritance condition in [32, Sect. 3.2]. However, the actual implementation in the modern Coq system accepts almost any function as in Definition 3.2 as a coercion.

Saïbi claimed that the uniform inheritance condition “ensures that any coercion can be applied to any object of its source class” [32, Sect. 3.2], but the actual condition ensures additional properties. The number and ordering of parameters of a uniform implicit coercion are the same as those of its source class; thus, convertibility checking of uniform implicit coercions \(f, g : C \rightarrowtail D\) does not require any special treatment such as permuting parameters of f and g. Moreover, function composition preserves this uniformity, that is, the following lemma holds.

Lemma 3.1

For any uniform implicit coercions \(f : C \rightarrowtail D\) and \(g : D \rightarrowtail E\), the function composition of the inheritance path \([f; g] : C \rightarrowtail E\) is uniform.

Proof

Let us assume that C, D, and E are classes with n, m, and k parameters respectively, and f and g have the following types:

$$\begin{aligned} f&: \forall (x_1 : T_1) \dots (x_n : T_n) \, (x_{n + 1} : C \, x_1 \dots x_n), D \, u_1 \dots u_m, \\ g&: \forall (y_1 : U_1) \dots (y_m : U_m) \, (y_{m + 1} : D \, y_1 \dots y_m), E \, v_1 \dots v_k. \end{aligned}$$

Then, the function composition of f and g can be defined and typed as follows:

figure cb

The terms \(u_1, \dots , u_m\) contain the free variables \(x_1, \dots , x_{n + 1}\) and we omitted substitutions for them by using the same names for the binders in the above definition. Nevertheless, \((g \circ f) : C \rightarrowtail E\) respects the uniform inheritance condition.    \(\square \)

In the above definition of the function composition \(g \circ f\) of implicit coercions, the types of \(x_1, \dots , x_n, x_{n + 1}\) and the parameters of g can be automatically inferred in Coq; thus, it can be abbreviated as follows:

figure cc

For implicit coercions \(f_1 : C_1 \rightarrowtail C_2, f_2 : C_2 \rightarrowtail C_3, \dots , f_n : C_n \rightarrowtail C_{n + 1}\) that have \(m_1, m_2, \dots , m_n\) parameters respectively, the function composition of the inheritance path \([f_1; f_2; \dots ; f_n]\) can be written as follows by repeatedly applying Lemma 3.1 and the above abbreviation.

figure cd

If \(f_1, \dots , f_n\) are all uniform, the numbers of their parameters \(m_1, \dots , m_n\) are equal to the numbers of parameters of \(C_1, \dots , C_n\). Consequently, the type inference algorithm always produces the typed closed term of most general function composition of \(f_1, \dots , f_n\) from the above term. If not all of \(f_1, \dots , f_n\) are uniform, type inference may fail or produce an open term, but if this produces a typed closed term, it is the most general function composition of \(f_1, \dots , f_n\).

Our coherence checking mechanism constructs the function composition of \(p : C \rightarrowtail C\) and compares it with the identity function of class C to check the first condition, and also constructs the function compositions of \(p, q : C \rightarrowtail D\) and performs the conversion test for them to check the second condition.

4 Automated Structure Inference

This section reviews how the automated structure inference mechanism [26] works on our example and in general. The first example is \(0 + 1\), whose desugared form is , where holes stand for implicit pieces of information to be inferred. The left- and right-hand sides of the top application can be type-checked without any use of canonical structures, as follows:

figure cg

where \(?_{\texttt {M}}\) and \(?_{\texttt {SR}}\) represent unification variables. Type-checking the application requires solving a unification problem , which is not trivial and which Coq does not know how to solve without hints. By declaring as a canonical instance, Coq can become aware of that instantiating \(?_{\texttt {M}}\) with is the canonical solution to this unification problem.

figure ck

The command takes a definition with a body of the form \(\lambda x_1 \, \dots \, x_n,\) \(\{|p_1 := (f_1 \dots ); \dots ; p_m := (f_m \dots )|\}\), and then synthesizes unification hints between the projections \(p_1, \dots , p_m\) and the head symbols \(f_1, \dots , f_m\), respectively, except for unnamed projections. Since has the following body, the above declaration synthesizes the unification hint between and that we need:

figure cq

In general, for any structures and such that inherits from with an implicit coercion , should be declared as a canonical instance to allow Coq to solve unification problems of the form by instantiating \(?_{\texttt {A}}\) with .

The second example is \(- 1\), whose desugared form is . The left- and right-hand sides of the top application can be type-checked as follows:

figure da

In order to type check the application, Coq has to unify with , which, again, is not trivial. Moreover, groups and semirings do not inherit from each other; therefore, this case is not an instance of the above criteria to define canonical instances. Nevertheless, this unification problem means that and are the same, and they are equipped with both group and semiring axioms, that is, the ring structure. Thus, its canonical solution should be introducing a fresh unification variable and instantiating \(?_{\texttt {G}}\) and \(?_{\texttt {SR}}\) with and , respectively. Right after defining , this unification hint can be defined as follows.

figure dj

This definition is definitionally equal to , but has a different head symbol, instead of , in its first field . Thus, the unification hint we need between and can be synthesized by the following declarations.

figure dq

This unification hint can also be defined conversely as follows. Whichever of those is acceptable, but at least one of them should be declared.

figure dr

For any structures A and B that have common (non-strict) subclasses \(\mathcal {C}\), we say that \(C \in \mathcal {C}\) is a join of A and B if C does not inherit from any other structures in \(\mathcal {C}\). For example, if we add the structure of commutative rings to the hierarchy of Sect. 2, the commutative ring structure is a common subclass of the group and semiring structures, but is not a join of them because the commutative ring structure inherits from the ring structure which is another common subclass of them. In general, the join of any two structures must be unique, and we should declare a canonical instance to infer the join C as the canonical solution of unification problems of the form . For any structures A and B such that B inherits from A, B is the join of A and B; thus, the first criteria to define canonical instances is just an instance of the second criteria.

Fig. 3.
figure 3

A minimal hierarchy that has ambiguous joins. Both structure and directly inherit from the structures and ; thus, and have two joins.

Fig. 4.
figure 4

A hierachy that disambiguates the join of and in Fig. 3 by redefining and to inherit from a new structure that inherits from and .

Figure 3 shows a minimal hierarchy that has ambiguous joins. If we declare that (resp.  ) is the canonical join of and in this hierarchy, it will also be accidentally inferred for a user who wants to reason about (resp.  ). Since and do not inherit from each other, inferred (resp.  ) can never be instantiated with (resp.  ); therefore, we have to disambiguate it as in Fig. 4, so that the join of and can be specialized to both and afterwards.

5 A Simplified Formal Model of Hierarchies

In this section, we define a simplified formal model of hierarchies and show a metatheorem that ensures the predictability of structure inference. First, we define the model of hierarchies and inheritance relations.

Definition 5.1

(Hierarchy and inheritance relations). A hierarchy \(\mathcal {H}\) is a finite set of structures partially ordered by a non-strict inheritance relation \(\leadsto ^*\); that is, \(\leadsto ^*\) is reflexive, antisymmetric, and transitive. We denote the corresponding strict (irreflexive) inheritance relation by \(\leadsto ^+\). \(A \leadsto ^* B\) and \(A \leadsto ^+ B\) respectively mean that B non-strictly and strictly inherits from A.

Definition 5.2

(Common subclasses). The (non-strict) common subclasses of \(A, B \in \mathcal {H}\) are \(\mathcal {C} := \{C \in \mathcal {H} \mid A \leadsto ^* C \wedge B \leadsto ^* C\}\). The minimal common subclasses of A and B is \(\mathrm {mcs}(A, B) := \mathcal {C} \setminus \{C \in \mathcal {H} \mid \exists C' \in \mathcal {C}, C' \leadsto ^+ C\}\).

Definition 5.3

(Well-formed hierarchy). A hierarchy \(\mathcal {H}\) is said to be well-formed if the minimal common subclasses of any two structures are unique; that is, \(|\mathrm {mcs}(A, B)| \le 1\) for any \(A, B \in \mathcal {H}\).

Definition 5.4

(Extended hierarchy). An extended hierarchy \(\bar{\mathcal {H}} := \mathcal {H} \mathbin {\dot{\cup }} \{\top \}\) is a hierarchy \(\mathcal {H}\) extended with \(\top \) which means a structure that strictly inherits from all the structures in \(\mathcal {H}\); thus, the inheritance relation is extended as follows:

figure ew

Definition 5.5

(Join). The join is a binary operator on an extended well-formed hierarchy \(\bar{\mathcal {H}}\), defined as follows:

figure ex

We encoded the above definitions on hierarchies in Coq by using the structure of partially ordered finite types of the mathcomp-finmap library [12] and proved the following theorem.

Theorem 5.1

The join operator on an extended well-formed hierarchy is associative, commutative, and idempotent; that is, an extended well-formed hierarchy is a join-semilattice.

If the unification algorithm of Coq respects our model of hierarchies and joins during structure inference, Theorem 5.1 implies that permuting, duplicating, and contracting unification problems do not change the result of inference; thus, it states the predictability of structure inference at a very abstract level.

6 Validating Well-Formedness of Hierarchies

This section presents a well-formedness checking algorithm that can also generate the exhaustive set of assertions for joins. We implemented this checking mechanism as a tool hierarchy.ml written in OCaml, which is available as a MathComp developer utility [45, /etc/utils/hierarchy.ml]. Our checking tool outputs the assertions as a Coq proof script which can detect missing and misimplemented unification hints for joins.

Since a hierarchy must be a finite set of structures (Definition 5.1), Definitions 5.2, 5.5, and 5.3 give us computable (yet inefficient) descriptions of joins and the well-formedness; in other words, for a given hierarchy \(\mathcal {H}\) and any two structures , one may enumerate their minimal common subclasses. Algorithm 1 is the checking algorithm we present, that takes in input an inheritance relation in the form of an indexed family of strict subclasses \(\texttt {subof}(A) := \{B \in \mathcal {H} \mid A \leadsto ^+ B\}\). The join function in this algorithm takes two structures as arguments, checks the uniqueness of their join, and then returns the join if it uniquely exists. In this function, the enumeration of minimal common subclasses is done by constructing the set of common subclasses \(\mathcal {C}\), and filtering out \(\texttt {subof}(C)\) from \(\mathcal {C}\) for every \(C \in \mathcal {C}\). In this filtering process, which is written as a foreach statement, we can skip elements already filtered out and do not need to care about ordering of picking up elements, thanks to transitivity.

The hierarchy.ml utility extracts the inheritance relation from a Coq library by interacting with coqtop, and then executes Algorithm 1 to check the well-formedness and to generate assertions. The assertions generated from our running example are shown below.

figure fa

An assertion asserts that the join of and is , and is implemented as a tactic that fails if the assertion is false. For instance, if we do not declare as a canonical instance, the assertion of line 9 fails and reports the following error.

figure fh
figure fi

One may declare incorrect canonical instances that overwrite an existing join. For example, the join of groups and monoids must be groups; however, defining the following canonical instance in the section overwrites this join.

figure fk

By declaring as a canonical instance, the join of and is still , but the join of and becomes , because of asymmetry of the unification mechanism. The assertion of line 1 fails and reports the following error.

figure fs

7 Evaluation

This section reports the results of applying our tools to the MathComp library 1.7.0 and on recent development efforts to extend the hierarchy in MathComp using our tools. MathComp 1.7.0 provides the structures depicted in Fig. 1, except and , and lacked a few of the edges; thus, its hierarchy is quite large. Our coherence checking mechanism found 11 inconvertible multiple inheritance paths in the ssralg library. Fortunately, those paths concern proof terms and are intended to be irrelevant; hence, we can ensure no implicit coercion breaks the modularity of reasoning. Our well-formedness checking tool discovered 7 ambiguous joins, 8 missing unification hints, and one overwritten join due to an incorrect declaration of a canonical instance. These inheritance bugs were found and fixed with the help of our tools; thus, similar issues cannot be found in the later versions of MathComp.

The first issue was that inheritance from the structures ( and its subclasses with the prefix ) to the structures ( and its subclasses) was not implemented, and consequently it introduced 7 ambiguous joins. For instance, did not inherit from as it should; consequently, they became ambiguous joins of and . 6 out of 8 missing unification hints should infer or structures. The other 2 unification hints are in numeric field ( and ) structures. Fixing the issue of missing inheritance from to was a difficult task without tool support. The missing inheritance itself was a known issue from before our tooling work, but the sub-hierarchy consisting of the , , and structures in Fig. 1 is quite dense; as a result, it prevents the library developers from enumerating joins correctly without automation [38].

The second issue was that the following canonical instance for overwrote the join of and , which should be .

figure gs

In this declaration, is a packager [26, Sect. 7] that takes a type and a mixin of as its arguments and construct a instance from the given mixin and the canonical instance for . However, if one omits its first argument with a placeholder as in the above, the packager may behave unpredictably as a unification hint. In the above case, the placeholder was instantiated with by type inference; as a result, it incorrectly overwrote the join of and .

Our tools can also help finding inheritance bugs when extending the hierarchy of MathComp, improve the development process by reducing the reviewing and maintenance burden, and allow developers and contributors to focus better on mathematical contents and other design issues. For instance, Hivert [24] added new structures of commutative algebras and redefined the field extension and splitting field structures to inherit from them. In this extension process, he fixed some inheritance issues with help from us and our tools; at the same time, we made sure there is no inheritance bug without reviewing the whole boilerplate code of structures. We ported the order sub-library of the mathcomp-finmap library [12] to MathComp, redefined numeric domain structures [10, Chap. 4] [11, Sect. 3.1] to inherit from ordered types, and factored out the notion of norms and absolute values as normed Abelian groups [1, Sect. 4.2] with the help of our tools [13, 36]. This modification resulted in approximately 10,000 lines of changes; thus, reducing the reviewing burden was an even more critical issue. This work is motivated by an improvement of the MathComp Analysis library [3] [30, Part II], which extends the hierarchy of MathComp with some algebraic and topological structures [1, Sect. 4] [30, Chap. 5] and is another application of our tools.

8 Conclusion and Related Work

This paper has provided a thorough analysis of the packed classes methodology, introduced two invariants that ensure the modularity of reasoning and the predictability of structure inference, and presented systematic ways to check those invariants. We implemented our invariant checking mechanisms as a part of the Coq system and a tool bundled with MathComp. With the help of these tools, many inheritance bugs in MathComp have been found and fixed. The MathComp development process has also been improved significantly.

Coq had no coherence checking mechanism before our work. Saïbi [32, Sect. 7] claimed that the coherence property “is too restrictive in practice” and “it is better to replace conversion by Leibniz equality to compare path coercions because Leibniz equality is a bigger relation than conversion”. However, most proof assistants based on dependent type theories including Coq still rely heavily on conversion, particularly in their type checking/inference mechanisms. Coherence should not be relaxed with Leibniz equality; otherwise, the type mismatch problems described in Sect. 3 will occur. With our coherence checking mechanism, users can still declare inconvertible multiple inheritance at their own risk and responsibility, because ambiguous paths messages are implemented as warnings rather than errors. The Lean system has an implicit coercion mechanism based on type class resolution, that allows users to define and use non-uniform implicit coercions; thus, coherence checking can be more difficult. Actually, Lean has no coherence checking mechanism; thus, users get more flexibility with this approach but need to be careful about being coherent.

There are three kinds of approaches to defining mathematical structures in dependent type theories: unbundled, semi-bundled, and bundled approaches [46, Sect. 4.1.1]. The unbundled approach uses an interface that is parameterized by carriers and operators, and gathers axioms as its fields, e.g., [41]; in contrast, the semi-bundled approach bundles operators together with axioms as in records, but still places carriers as parameters, e.g., [46]. The bundled approach uses an interface that bundles carriers together with operators and axioms, e.g., packed classes and telescopes [26, Sect. 2.3] [9, 18, 29]. The above difference between definitions of interfaces, in particular, whether carriers are bundled or not, leads to the use of different instance resolution and inference mechanisms: type classes [22, 40] for the unbundled and semi-bundled approaches, and canonical structures or other unification hint mechanisms for the bundled approach. Researchers have observed unpredictable behaviors [23] and efficiency issues [46, Sect. 4.3] [41, Sect. 11] in inference with type classes; in contrast, structure inference with packed classes is predictable, and Theorem 5.1 states this predictability more formally, except for concrete instance resolution. The resolution of canonical structures is carried out by consulting a table of unification hints indexed by pairs of two head symbols and optionally with its recursive application and backtracking [21, Sect. 2.3]. The packed classes methodology is designed to use this recursive resolution not for structure inference [17, Sect. 2.3] but only for parametric instances [26, Sect. 4] such as lists and products, and not to use backtracking. Thus, there is no efficiency issue in structure inference, except that nested class records and chains of their projections exponentially slow down the conversion which flat variant of packed classes [14, Sect. 4] can mitigate. In the unbundled and semi-bundled approaches, a carrier may be associated with multiple classes; thus, inference of join and our work on structure inference (Sect. 4, 5, and 6) are problems specific to the bundled approach. A detailed comparison of type classes and packed classes has also been provided in [1]. There are a few mechanisms to extend the unification engines of proof assistants other than canonical structures that can implement structure inference for packed classes: unification hints [4] and coercions pullback [31]. For any of those cases, our invariants are fundamental properties to implement packed classes and structure inference, but the invariant checking we propose has not been made yet at all.

Packed classes require the systematic use of records, implicit coercions, and canonical structures. This leads us to automated generation of structures from their higher-level descriptions [14], which is work in progress.