MetaPRL Tutorial: Quantifiers

Introduction

The declaration of the quantifiers exists and all require the introduction of a universe of formulas to quantify over. At this point, the issue of predicativity comes into play:

The Nuprl type theory adopts the predicative definition, and formulas are stratified into type universes so that no non-trivial type is a member of itself. For this logic, we adopt an impredicative theory, which restricts the models of the logic, but increases its expressiveness. To start, we need to define the universe of formulas in the Fol_univ module.

The Fol_univ interface

The interface for the Fol_univ module defines the formula universe term.

1. Define the universe of formulas in the file fol_univ.mli as follows:

extends Fol_type
declare univ

The Fol_univ implementation

The implementation contains the usual definitions for the syntax and display forms.

2. Define the universe syntax in the file fol_univ.ml:

extends Fol_type

declare univ

dform univ_df : except_mode[src] :: univ = `"Univ"

Any element of the universe of formulas is a well-defined formula. We can express this in the following rule:

3. Define well-formedness of quantified formulas:

prim univ_type 'H 'J : :
   sequent ['ext] { 'H; x: univ; 'J['x] >- "type"{'x} } = trivial

This rule is a little different that the previous rules: it requires a particular terms in both the hypothesis and the conclusion. The application of this rule is context-sensitive, so it is not a good candidate for automation with the dT tactic. Instead, we provide a custom tactic to perform universal reasoning.

4. Define a tactic for reasoning about universal quantification:

let nthUnivT i p =
   let j, k = Sequent.hyp_indices p i in
      univ_type j k p

This tactic applies the univ_type rule to a specific hypothesis. We can automate the application of this tactic, not through the dT tactic, but through the trivialT tactic. The trivialT tactic is defined in the module Auto_tactic, and it performs "trivial" reasoning, such as proof by assumption, as is the case here. The code to add univ_type to the trivial tactic has the same general format as adding to the dT tactic: we improve the trivial_resource.

5. Automate universal reasoning with the trivialT resource:

let trivial_resource =
   Mp_resource.resource_improve trivial_resource (**)
      { auto_name = "nthUnivT";
        auto_prec = trivial_prec;
        auto_tac = onSomeHypT nthUnivT
      }

The auto_name is the name of the addition (used for debugging). The trivial_prec is the precedence of the entry. The trivialT resource contains several tactics, and tactics with lower precedence are tried first. The precedences are declared with the standard prec form. The final value, auto_tac, is the tactic to apply for trivial reasoning. The onSomeHypT tactic tries to apply the nthUnivT to one of the hypotheses in the sequent.

The Fol_all interface

The definition of the universal quantifier uses the same proof terms as the Fol_implies theory, and so the interface establishes a dependency between the theories, as well as the Fol_univ theory.

6. Declare the logical dependencies and syntax of the universal quantifier in the file fol_all.mli:

extends Fol_implies
extends Fol_univ

declare "all"{x. 'B['x]}

prec prec_all

The Fol_all implementation

The syntax and display forms for the all term have the standard form.

7. Declare the syntax and display for the all term in the fol_all.ml file:

extends Fol_implies
extends Fol_univ

declare "all"{x. 'B['x]}

prec prec_all

dform all_df : except_mode[src] :: parens :: "prec"["prec_all"] :: "all"{x. 'B} =
   szone pushm[3] forall slot{'x} `"." hspace slot{'B} popm ezone

The rules for the all term introduce the quantification over the universe term univ.

8. Define the typehood, introduction, and elimination forms in the fol_all.ml file:

prim all_type 'H 'x :
   sequent ['ext] { 'H; x: univ >- "type"{'B['x]} } -->
   sequent ['ext] { 'H >- "type"{."all"{y. 'B['y]}} } = trivial

prim all_intro 'H 'x :
   ('b['x] : sequent ['ext] { 'H; x: univ >- 'B['x] }) -->
   sequent ['ext] { 'H >- "all"{y. 'B['y]} } =
   lambda{y. 'b['y]}

prim all_elim 'H 'J 'x 'z 'a :
   sequent ['ext] { 'H; x: "all"{y. 'B['y]}; 'J['x] >- "type"{'a} } -->
   ('b['x; 'z] : sequent ['ext] { 'H; x: "all"{y. 'B['y]}; 'J['x]; z: 'B['a] >- 'C['x] }) -->
   sequent ['ext] { 'H; x: "all"{y. 'B['y]}; 'J['x] >- 'C['x] } =
   'b['x; 'x 'a]

The rule for all_elim can take some explanation. The rule states that if the formula all{y. 'B['y]} is true by assumption, then it is true on any formula 'a.

The automation for the universal quantifier takes care to restrict the introduction of new variables. Before defining the tactics for automation, we first define operations on the terms used to define universal quantification. The term functions are defined in the Refiner.Refiner.Term and Refiner.Refiner.TermOp modules.

9. Define term functions for the universal quantifier.

let all_term = << "all"{y. 'B['y]} >>
let all_opname = opname_of_term all_term
let is_all_term = is_dep1_term all_opname
let dest_all = dest_dep1_term all_opname

The opname_of_term function returns the operator name of the quantifier, which is used to define two terms: the is_all_term function tests if a term is a universal quantifier, and the dest_all function returns the bound variable and body of a universal quantifier (or raise a RefineError exception if the term does not have the right form).

We use these functions to define the tactic for well-formedness of a universal quantification.

10. Define the tactic for proving well-formedness of the universal quantifier:

let d_all_type i p =
   if i = 0 then
      let t = dest_type (Sequent.concl p) in
      let v, _ = dest_all t in
      let v = Var.maybe_new_vars1 p v in
         all_type (Sequent.hyp_count_addr p) v p
   else
      raise (RefineError ("d_all_type", StringError "no elimination form"))
let all_type_term = << "type"{."all"{x. 'B['x]}} >>
let d_resource = Mp_resource.resource_improve d_resource (all_type_term, d_all_type)

This function computes the new variable 'x by producing a new variable from the quantified universal variable.

The tactic for applying introduction and elimination reasoning has a similar definition.

11. Define the tactic for introduction and elimination reasoning:

let d_all i p =
   if i = 0 then
      let goal = Sequent.concl p in
      let v, _ = dest_all goal in
      let v = Var.maybe_new_vars1 p v in
         all_intro (Sequent.hyp_count_addr p) v p
   else
      let x, a = Sequent.nth_hyp p i in
      let v, _ = dest_all a in
      let v = Var.maybe_new_vars1 p v in
      let y = get_with_arg p in
      let j, k = Sequent.hyp_indices p i in
         all_elim j k x v y p

let d_resource = Mp_resource.resource_improve d_resource (all_term, d_all)

The introduction and eliminations computations both produce a new variable from the existing quantified variable. The elimination form also uses the get_with_arg function to get the "optional" argument provided by the user for instantiation. When the dT tactic is used for universal elimination, the user must provide this argument using the withT tactical, like the following:

refine withT << "true >> (dT 2)