• No results found

Linear type checking in Coq

One of the hardest parts of implementing a linear embedded language is checking the lin- earity constraints. As we saw in Chapter 4, linear type checking is multi-directional, as some typing contexts must be checked and others must be inferred in any particular typing derivation.

For example, consider type checking the judgment ∆′1 ⊢Q gate p2 ← g # p1; c ∶ σ.

Although we know the value of the typing context ∆′1, in order to apply the types_gate constructor, we have to guess the values of the typing contexts ∆ and ∆1. From the value

ofp1, we will be able to infer the value of ∆1; we can then use the judgment∆1'== ∆1● ∆ to infer the value of ∆.

In Coq we can facilitate this sort of bidirectional type checking using EVars, unknown variables that will be filled in over the course of a proof with concrete values.

In order to solve goals of linear type constraints, Robert Rand and I developed a stan- dalone library for automatically solving linearity constraints, available on GitHub.15 The library contains a theory of linear typing contexts and automation techniques for discharging goals of linearity constraints in Coq.

8.2.1 Partial commutative monoids

The library is based on the theory of typing contexts as a partial commutative monoids (PCM) (Wehrung, 2017). A PCM is a commutative monoid with a zero undefined element satisfying certain laws.

Definition 8.2.1. A commutative monoid (α,⊺,●) consists of a type α with an element ⊺ ∶α and a binary operation ● ∶α→α→α satisfying:

⊺ ●a=a (●-⊺)

a● (b●c) = (a●b) ●c (●-assoc)

a●b=b●a (●-commute)

15

Apartial commutative monoid(α,⊺,–,●) consists of a commutative monoid(α,⊺,●), along with an element – ∶α representing undefined values, such that:

a● – = – (●-–)

Example 8.2.2. Let IdxMap be the type list(optionWType), representing a partial map from indices i in the list ∆ to wire types ∆[i]. We define the partial merge operation mergeIdxMap on IdxMap as follows:

Fixpoint mergeIdxMap (i1 i2 : IdxMap) : option IdxMap :=

match i1, i2 with

| [], _ ⇒ Some i2

| _, [] ⇒ Some i1

| None :: i1', None :: i2' ⇒ consOption None (mergeIdxMap i1' i2') | Some σ1 :: i1', None :: i2' ⇒ consOption (Some σ1) (mergeIdxMap i1' i2') | None :: i1', Some σ2 :: i2' ⇒ consOption (Some σ2) (mergeIdxMap i1' i2') | Some _ :: _, Some _ :: _ ⇒ Nothing

end.

where consOption a ls maps (cons a)over ls:option(list α).

We write IdxCtxforoptionIdxMap, which forms a partial commutative monoid with the

following components:

⊺ := Some []

– := Nothing

∆1 ● ∆2 := match ∆1, ∆2 with

| Some i1, Some i2 ⇒ mergeIdxMap i1 i2 | _, _ ⇒ Nothing

end

The structure of a PCM tells us how to solve goals of the form a=b, where a, b∶α are permutations and associations of the same set of elements. The Coq tactic monoid solves

goals of this form, such as:

Lemma PCM_abc : ∀ a b c, a ● b ● c = c ● a ● ⊺ ● b. Proof. intros. monoid. Qed.

Because we will sometimes be reasoning about unknown contexts represented as EVars, themonoidtactic can solve goals with at most oneEVar. Here, the tacticeexistsintroduces an EVar, which themonoid tactic fills in with b.

Lemma PCM_evar : ∀ a b, exists unknown, unknown ● a = a ● b. Proof. intros. eexists. monoid. Qed.

The monoid tactic is based on Chlipala’s reflection technique (Chlipala, 2013, Chapter 15), and we have extended it to partial commutative monoids that may also contain EVars.

8.2.2 Validity

We say that a typing context isvalid if it is not equal to the undefined element–. Instead of reasoning directly about the negative assertion∆≠ –, we reformulate this judgment as a positive assertion is_valid∆.

Definition is_valid ∆ := ∆ <> –. Validity satisfies the following properties:

- ⊺is valid; and

- ∆1 ●∆2 ●∆3 is valid if and only if (∆1 ●∆2), (∆1 ●∆3), and (∆2 ●∆3) are all valid. Notice that the merge of ∆1 and ∆2 should always be valid provided their domains

are disjoint. To capture this fact, we introduce two additional properties that describe the validity of singleton typing contexts of the form singleton xτ.

- every singleton contextsingleton x σis valid; and

- singleton xσ●singleton y τ is valid if and only if x≠y.

Example 8.2.3. The kindIdxCtx≡optionIdxMapis well-formed, where a singleton context is defined as

singleton 0 σ ≡ Some [Some σ]

singleton (n+1) σ ≡ consOption None (singleton n σ)

The tacticvalidatesolves goals of the formis_valid∆. Unlikemonoid,validatecannot handle goals that contain an EVar, which might be filled in with the undefined element–.

Lemma valid_test : ∀ x y z a b c, x <> y → y <> z → x <> z → is_valid (singleton x a ● singleton y b ● singleton z c). Proof. validate. Qed.

In the typing rules, we often combine judgments of the form ∆=∆1●∆2andis_valid∆,

so we introduce notation for such goals.

Notation "∆ == ∆1 ● ∆2" := (is_valid ∆ ∧ ∆ = ∆1 ● ∆2) (at level 75). The tacticsolve_ctxsolves goals of this form.

Ltac solve_ctx := split; [validate | monoid].

8.2.3 The type check tactic.

The type_check tactic uses solve_ctx and other tactics to discharge goals of the form ∆⇒Qp:σ, ∆⊢Qc:τ, ∆ ⊢Qf:Fun, and Typed_Box b. It does this by repeatedly calling

econstructor, which automatically applies a constructor from the appropriate inductively- defined data type, introducing EVars for the values of unknown typing contexts. The

type_check tactic will not apply induction or other high-level proof techniques, but if a circuit is concrete, it can discharge the goal automatically in most cases.