What does the `with` keyword without the `match` do inside a inductive type in Coq? - coq

What does the with keyword without the match do inside a inductive type in Coq?, example:
Inductive Block : Type :=
| EmptyBlk : Block
| Blk : Statement -> Block
with Statement : Type :=
| Assignment : string -> AExp -> Statement
| Seq : Statement -> Statement -> Statement
| IfElse : BExp -> Block -> Block -> Statement
| While : BExp -> Block -> Statement.
I tried checking the type of Statement and it seems its not of type block or something...So what is the point of defining it inside another inductive type rather than by itself. At least checking the type of Statement gives Set the same as for Block...

It is used to specify mutually recursive definitions. For example, consider the following two functions:
Fixpoint even (n : nat) : bool :=
match n with
| O => true
| S n => odd n
end
with odd (n : nat) : bool :=
match n with
| O => false
| S n => even n
end.
Here, you cannot define even first because it needs odd to be defined. You cannot define odd first either because it needs even. You need to be able to define both at the same time - and you do that by using the with keyword.
Your example is similar but defines inductive datatype rather than recursive function - Statement uses Block in its definition and Block uses Statement. Hence, with to define them both at the same time.
Note that this with is completely different keyword than with from the match expressions. In fact, they belong to two different languages: the former one is part of Vernacular whereas the latter is part of Gallina.

Related

Why is my definition not allowed because of strict positivity?

I have the following two definitions that result in two different error messages.
The first definition is declined because of strict positivity and the second one because of a universe inconsistency.
(* non-strictly positive *)
Inductive SwitchNSP (A : Type) : Type :=
| switchNSP : SwitchNSP bool -> SwitchNSP A.
Fail Inductive UseSwitchNSP :=
| useSwitchNSP : SwitchNSP UseSwitchNSP -> UseSwitchNSP.
(* universe inconsistency *)
Inductive SwitchNSPI : Type -> Type :=
| switchNSPI : forall A, SwitchNSPI bool -> SwitchNSPI A.
Fail Inductive UseSwitchNSPI :=
| useSwitchNSPI : SwitchNSPI UseSwitchNSPI -> UseSwitchNSPI.
Chatting on gitter revealed that universe (in)consistencies are checked first, that is, the first definition adheres this check, but then fails because of a strict positivity issue.
As far as I understand the strict positivity restriction, if Coq allows non-strictly positivity data type definitions, I could construct non-terminating functions without using fix (which is pretty bad).
In order to make it even more confusing, the first definition is accepted in Agda and the second one gives a strict positivity error.
data Bool : Set where
True : Bool
False : Bool
data SwitchNSP (A : Set) : Set where
switchNSP : SwitchNSP Bool -> SwitchNSP A
data UseSwitchNSP : Set where
useSwitchNSP : SwitchNSP UseSwitchNSP -> UseSwitchNSP
data SwitchNSPI : Set -> Set where
switchNSPI : forall A -> SwitchNSPI Bool -> SwitchNSPI A
data UseSwitchNSPI : Set where
useSwitchNSP : SwitchNSPI UseSwitchNSPI -> UseSwitchNSPI
Now my question is two-folded: first, what is the "evil example" I could construct with the above definition? Second, which of the rules applies to the above definition?
Some notes:
To clarify, I think that I do understand why the second definition is not allowed type-checking-wise, but still feel that there is nothing "evil" happening here, when the definition is allowed.
I first thought that my example is an instance of this question, but enabling universe polymorphism does not help for the second definition.
Can I use some "trick" do adapt my definition such that it is accepted by Coq?
Unfortunately, there's nothing super deep about this example. As you noted Agda accepts it, and what trips Coq is the lack of uniformity in the parameters. For example, it accepts this:
Inductive SwitchNSPA (A : Type) : Type :=
| switchNSPA : SwitchNSPA A -> SwitchNSPA A.
Inductive UseSwitchNSPA :=
| useSwitchNSPA : SwitchNSPA UseSwitchNSPA -> UseSwitchNSPA.
Positivity criteria like the one used by Coq are not complete, so they will reject harmless types; the problem with supporting more types is that it often makes the positivity checker more complex, and that's already one of the most complex pieces of the kernel.
As for the concrete details of why it rejects it, well, I'm not 100% sure. Going by the rules in the manual, I think it should be accepted?
EDIT: The manual is being updated.
Specifically, using the following shorter names to simplify the following:
Inductive Inner (A : Type) : Type := inner : Inner bool -> Inner A.
Inductive Outer := outer : Inner Outer -> Outer.
Correctness rules
Positivity condition
Here,
X = Outer
T = forall x: Inner X, X
So we're in the second case with
U = Inner X
V = X
V is easy, so let's do that first:
V = (X) falls in the first case, with no t_i, hence is positive for X
For U: is U = Inner X strictly positive wrt X?
Here,
T = Inner X
Hence we're in the last case: T converts to (I a1) (no t_i) with
I = Inner
a1 = X
and X does not occur in the t_i, since there are no t_i.
Do the instantiated types of the constructors satisfy the nested positivity condition?
There is only one constructor:
inner : Inner bool -> Inner X.
Does this satisfy the nested positivity condition?
Here,
T = forall x: Inner bool, Inner X.
So we're in the second case with
U = Inner bool
V = Inner X
X does not occur in U, so X is strictly positive in U.
Does V satisfy the nested positivity condition for X?
Here,
T = Inner X
Hence we're in the first case: T converts to (I b1) (no u_i) with
I = Inner
b1 = X
There are no u_i, so V satisfies the nested positivity condition.
I have opened a bug report. The manual is being fixed.
Two more small things:
I can't resist pointing that your type is empty:
Theorem Inner_empty: forall A, Inner A -> False.
Proof. induction 1; assumption. Qed.
You wrote:
if Coq allows non-strictly positivity data type definitions, I could construct non-terminating functions without using fix (which is pretty bad).
That's almost correct, but not exactly: if Coq didn't enforce strict positivity, you could construct non-terminating functions period, which is bad. It doesn't matter whether they use fix or not: having non-termination in the logic basically makes it unsound (and hence Coq prevents you from writing fixpoints that do not terminate by lazy reduction).

Defining the syntax with constraints

I want to define the syntax of the time delay in Coq as follows:
Inductive tcs : Type :=
| delay : nat -> tcs
| atomic : nat-> tcs.
But the natural number 0 is not allowed in the constructor delay. I also find using the subset type to define the syntax. However, the introduction of subset type leads to more complications for defining semantics. Is there an easy way to define such sytax?
In your particular case, I think the easiest thing to do is to change the interpretation of the number in the delay constructor: note that nat is isomorphic to {n : nat | n > 0} via the successor function. Therefore, all you have to do is add one to the argument of delay whenever you use it in a match expression. For instance:
Definition nat_of_tcs t :=
match t with
| delay n => S n
| atomic n => n
end.
Alternatively, you can replace nat by positive, a type defined in the standard library to represent numbers greater than 0.
In general, subset types would be the way to go. You could add a n != 0 constraint in the type of delay, or define a separate predicate on elements of tcs describing when elements are well-formed:
Definition well_formed t :=
match t with
| delay 0 => false
| _ => true
end.
Definition wftcs := { t : tcs | well_formed t = true }.
The Mathematical Components and related libraries use this pattern pervasively; check the subType class in the eqtype module, for instance. In my extensional structures library, I use the subType class to define a type of finite sets (here) by packaging a list with a proof that the list is sorted.

Why FMapAVL usage in argument is non strictly positive while list is?

Consider the following code:
Require Import FMapAVL.
Require Import Coq.Structures.OrderedTypeEx.
Module NatMap := FMapAVL.Make(Nat_as_OT).
Inductive ttree (K : Type) (V : Type) :=
| tleaf : ttree K V
| tnode : ttree K V -> K -> V -> ttree K V -> nat -> ttree K V.
Inductive test :=
| test1 : test
| test2 : ttree nat test -> test
| test3 : list test -> test
| test4 : NatMap.t test -> test.
In Coq 8.6, I get Error: Non strictly positive occurrence of "test" in "NatMap.t test -> test". I get no error without test4.
Why does applying a NatMap.t (FMapAVL with nat keys) constructor to my test inductive type creates a non strictly positive occurence while applying list constructor or even ttree constructor (which is just like internal structure of FMapAVL) okay?
What are the common workarounds if I want something like test4 from my example, preferably ones not requiring me to make my own map implementation like that ttree?
The problem is that Coq can't handle some higher-order inductive types as nested inductives - I'm not convinced I fully understand the limitations, but I investigated a bit.
One important fact that helps explain the behavior is that Coq has special support for passing an inductive type to a type constructor. CPDT's Inductive Types chapter explains this in the section on Nested Inductive Types: Coq creates a version of list or ttree specialized to test and pretends you're defining tree and these specialized inductives with mutual induction. This generally works fine (such as for your list and even ttree definitions). It even works for modules, as long as they use "transparent ascription" (and FMapAVL.Make does so). However, it seems to break down when the type is an index instead of a parameter (that is, when the Type is to the right of the colon instead of the left):
Module Type Transformer.
Axiom T:Type -> Type.
End Transformer.
Module IdOpaque : Transformer.
Definition T (t:Type) := t.
End IdOpaque.
Inductive transformer : Type -> Type :=
| MkT : forall t, t -> transformer t .
(* make the argument a parameter *)
Inductive transformer' (t:Type) : Type :=
| MkT' : t -> transformer' t.
Module IdInd <: Transformer.
Definition T : Type -> Type := transformer.
End IdInd.
Module IdTransparent <: Transformer.
Definition T (t:Type) : Type := t.
End IdTransparent.
(* works with a simple definition, even inside a module, as long as its
transparent *)
Inductive test1 :=
| mkTest1 (_:IdTransparent.T test1).
(* not strictly positive (Coq can't see definition) *)
Fail Inductive test2 :=
| mkTest2 (_:IdOpaque.T test2).
(* this is pretty much what happens with FMapAVL.Make *)
Fail Inductive test3 :=
| mkTest3 (_:IdInd.T test3).
(* even this fails *)
Fail Inductive test4 :=
| mkTest4 (_:transformer test4).
(* this finally works *)
Inductive test5 :=
| mkTest5 (_:transformer' test5).

Indicator function and semirings in COQ

I'm quite new with Coq, and I'm trying to define a "generic" indicator function, like this :
Function indicator (x : nat) : bool :=
match x with
| O => false
| _ => true
end.
This one works well.
My problem is that I want an indicator function that returns false for the identity element of any semiring (for which I have a personal definition), not just for the natural number zero, like this :
Function indicator `(S : Semiring) (x : K) : bool :=
match x with
| ident => false
| _ => true
end.
where K is defined in the semiring S as the set and ident is defined in the semiring Sas the identity element.
This one doesn't work. I got the error:
This clause is redundant
with the last line of the match underlined. However, I don't think the error really comes from here. I read that it may come from the line
| ident => false
because ident is a variable, but I don't have more clues.
nat is an inductive type, created from two constructors:
Inductive nat: Set :=
| O : nat
| S : nat -> nat
.
This means that any inhabitant of the nat type is always built by a combination of these 2 constructors.
You can "inspect" this construction by pattern matching, like you did in the first indicator definition.
In the second case, your type K is a type variable (you don't want to have a fix type like nat), so you didn't explain how to build elements of K. Then, when you pattern match, the ident your wrote is just a binder, any name would have had the same effect (and _ too). It has no link to the indent of your semiring. Coq said that the clause is redundant because ident already captured any element of type K, so the _ case is never called.
If you want to write such a function for any type K, you will have to provide a way to compare elements of K (something of the type K -> K -> bool) and use it in your indicator function. I'm not sure about the syntax but you'll have something link:
Record SemiRing : Type := mkSemiRing {
K: Type;
ident : K;
compare : K -> K -> bool;
(* you might need the property that:
forall x y, compare x y = true -> x = y
*)
op1 : K -> K -> K;
op2 : K -> K -> K
(* and all the laws of semiring... *)
}.
Definition indicator (ring: SemiRing) (x: K ring) : bool :=
if compare ring x (ident ring) then true else false.

Record and Definition

I have a question about the: Record and Definition
I have this definition:
Definition rule := term -> term.
and I write a boolean function for it.
Definition beq_rule a b := beq_term a && beq_term b.
where beq_term : term -> term -> bool.
so my definition of beq_rule actually return exactly type of beq_term which is not what I want here. I want it return for me a type: rule -> rule -> bool.
So I changed a definition of rule by Record:
Record rule := mkRule {lhs : term; rhs : term}.
and
Definition beq_rule (a b : rule) : bool :=
beq_term (lhs a) (lhs b) && beq_term (rhs a) (rhs b).
My question is that:
1) What is the different between my first defined rule used Definition and another used Record?
2) If I want to define rule by Definition can I give an alias lhs and rhs likes in Record definition?
Your two definitions of rule are saying totally different things
Definition rule := term -> term
is defining rule as a type (or Prop) alias of the function type term -> term. Hence
Definition not_what_you_meant : rule := fun t => t.
will happily compile.
As to the relation between Record and Definition. Record is just a macro that converts into an Inductive. So
Record rule := mkRule {lhs : term; rhs : term}.
is the same as
Inductive rule := mkRule : term -> term -> rule.
plus accessor functions
Definition lhs (r : rule) : term :=
match r with
mkRule l _ => l
end.
etc.
You should think of Inductive as being fundamentally different from Definition. Definition defines an alias. Another way f saying this is Definitions are "referentially transparent", you can (up to variable renaming) always substitute the right hand side of a definition for any occurrence of its name.
Inductive on the other hand defines type (elements of Coqs universes) by listing off a set of constructors. In more logical way of thinking, Inductive defines a logical proposition in terms of its elimination/introduction rules in a way that ensures "harmony".