Is there a way to define subtype relationship in Coq?
I read about subset typing, in which a predicate is used to determine what goes into the subtype, but this is not what I am aiming for. I just want to define a theory in which there is a type (U) and another type (I), which is subtype of (U).
There is no true subtyping in Coq (except for universe subtyping, which is probably not what you want). The closest alternative is to use coercions, which are functions that the Coq type checker inserts automatically whenever it is expecting an element of one type but finds an element of another type instead. For instance, consider the following coercion from booleans to natural numbers:
Definition nat_of_bool (b : bool) : nat :=
if b then 1 else 0.
Coercion nat_of_bool : bool >-> nat.
After running this snippet, Coq uses nat_of_bool to convert bool to nat, as shown here:
Check true + 3.
(* true + 3 : nat *)
Thus, bool starts behaving almost as if it were a subtype of nat.
Though nat_of_bool does not appear here, it is just being hidden by Coq's printer. This term is actually the same thing as nat_of_bool true + 3, as we can see by asking Coq to print all coercions:
Set Printing Coercions.
Check true + 3.
(* nat_of_bool true + 3 : nat *)
The :> symbol you had asked about earlier, when used in a record declaration, is doing the same thing. For instance, the code
Record foo := Foo {
sort :> Type
}.
is equivalent to
Record foo := Foo {
sort : Type
}.
Coercion sort : foo >-> Sortclass.
where Sortclass is a special coercion target for Type, Prop and Set.
The Coq user manual describes coercions in more detail.
Related
I've defined the following type which has the exact same structure as option.
Inductive optionX A := NoneX | SomeX (a : A).
While Check Some 1 correctly infers the type parameter A, Check SomeX 1 produces the message
The term "1" has type "nat" while it is expected to have type "Type".
Am I missing something? Note that it works fine if I explicitly provide the type parameter: Check SomeX(nat) 1.
Coq has an implicit arguments mechanism for this kind of situation:
Inductive optionX A := NoneX | SomeX (a : A).
Arguments NoneX {A}.
Arguments SomeX {A} a. (* Curly braces means "please try to infer this" *)
Check SomeX 1.
(* You can turn off argument inference with # *)
Check #NoneX nat. (* optionX nat *)
Note that the expression that you had earlier, SomeX(nat) 1, is parsed the same way as SomeX nat 1, which is the constant SomeX applied to the arguments nat and 1. The mechanism for supplying type arguments is the same as how you supply other kinds of arguments.
The reference manual has more information on implicit arguments.
I'm trying to write a coercion of a specific sigma type into a binary relation. The coercion works when using a coercible term as a function argument, but not when using the same term as a function.
Here is the code:
Definition relation A := A -> A -> Prop.
Definition is_serial {A} (R: relation A) := forall x, exists y, R x y.
Definition serial state := {R: relation state | is_serial R}.
Definition serial_to_relation A (s: serial A) : relation A := proj1_sig s.
Coercion serial_to_relation : serial >-> relation.
Parameter foo: serial nat.
Parameter bar: relation nat -> Prop.
(* Succeeds in coercing an argument. *)
Check (bar foo).
(* Fails to coerce when applying arguments to coercible term *)
Fail Check (foo 1 2).
Here's an executable version of the snippet: https://x80.org/collacoq/udutosoher.coq
Why does the second check fail? Did I make a mistake in my coercion definition? Or is this some limitation of coercion inference that I'm unaware of?
I think the problem in the last case is that Coq tries to infer a function type for foo and relation A is not literally a function type (only up to unfolding of the definition of relation).
If you add a coercion to Funclass (see the doc) as follows then the last test goes through
Definition serial_to_fun A (s: serial A) : A -> A -> Prop := proj1_sig s.
Coercion serial_to_fun : serial >-> Funclass.
Well. It is just not able to "understand" that you want it to coerce to relation nat.
When you write Check (bar foo). coq should unify type which the bar is expecting (namely relation nat) and the type which foo has (namely serial nat). So, coq tryes to unify serial nat and relation nat and since these types are not convertable, coq "understands" that it needs try to use coercion. So, it tryes to find coercion from serial nat to relation nat. And since you have provided such a coercion, it succeeds.
Edit.
When you write Check (foo 1 2). coq will try to unify type of the foo (namely serial nat) and type of function like (nat -> nat -> A) . So, again, it just does able not see that you want it to coerce foo to relation nat.
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).
I've trouble understanding the (point of the) gauntlet one has to pass to bypass the uniform inheritance condition (UIC). Per the instruction
Let /.../ f: forall (x₁:T₁)..(xₖ:Tₖ)(y:C u₁..uₙ), D v₁..vₘ be a
function which does not verify the uniform inheritance condition. To
declare f as coercion, one has first to declare a subclass C' of C
/.../
In the code below, f is such a function:
Parameter C: nat -> Type.
Parameter D: nat -> Prop.
Parameter f: forall {x y}(z:C x), D y.
Parameter f':> forall {x y}(z:C x), D y. (*violates uic*)
Print Coercions. (* #f' *)
Yet I do not have to do anything except putting :> to declare it as a coercion. Maybe the gauntlet will somehow help to avoid breaking UIC? Not so:
Definition C' := fun x => C x.
Fail Definition Id_C_f := fun x d (y: C' x) => (y: C d). (*attempt to define Id_C_f as in the manual*)
Identity Coercion Id_C_f: C' >-> C.
Fail Coercion f: C' >-> D. (*Cannot recognize C' as a source class of f*)
Coercion f'' {x y}(z:C' x): D y := f z. (*violates uic*)
Print Coercions. (* #f' #f'' Id_C_f *)
The question: What am I missing here?
I've trouble understanding the (point of the) gauntlet one has to pass to bypass the uniform inheritance condition (UIC).
Intuitively, the uniform inheritance condition says (roughly) "it's syntactically possible to determine every argument to the coercion function just from the type of the source argument".
The developer that added coercions found it easier (I presume) to write the code implementing coercions if the uniform inheritance condition is assumed. I'm sure that a pull request relaxing this constraint and correctly implementing more general coercions would be welcomed!
That said, note that there is a warning message (not an error message) when you declare a coercion that violates the UIC. It will still add it to the table of coercions. Depending on your version of Coq, the coercion might never trigger, or you might get an error message at type inference time when the code applying the coercion builds an ill-typed term because it tries to apply the coercion assuming the UIC holds when it actually doesn't, or (in older versions of Coq) you can get anomalies (see, e.g., bug reports #4114, #4507, #4635, #3373, and #2828).
That said, here is an example where Identity Coercions are useful:
Require Import Coq.PArith.PArith. (* positive *)
Require Import Coq.FSets.FMapPositive.
Definition lookup {A} (map : PositiveMap.t A) (idx : positive) : option A
:= PositiveMap.find idx map.
(* allows us to apply maps as if they were functions *)
Coercion lookup : PositiveMap.t >-> Funclass.
Definition nat_tree := PositiveMap.t nat.
Axiom mymap1 : PositiveMap.t nat.
Axiom mymap2 : nat_tree.
Local Open Scope positive_scope. (* let 1 mean 1:positive *)
Check mymap1 1. (* mymap1 1 : option nat *)
Fail Check mymap2 1.
(* The command has indeed failed with message:
Illegal application (Non-functional construction):
The expression "mymap2" of type "nat_tree"
cannot be applied to the term
"1" : "positive" *)
Identity Coercion Id_nat_tree : nat_tree >-> PositiveMap.t.
Check mymap2 1. (* mymap2 1 : option nat *)
Basically, in the extremely limited case where you have an identifier which would be recognized as the source of an existing coercion if you unfolded its type a bit, you can use Identity Coercion to do that. (You can also do it by defining a copy of your existing coercion with a different type signature, and declaring that a coercion too. But then if you have some lemmas that mention one coercion, and some lemmas that mention the other, rewrite will have issues.)
There is one other use case for Identity Coercions, which is that, when your source is not an inductive type, you can use them for folding and not just unfolding identifiers, by playing tricks with Modules and Module Types; see this comment on #3115 for an example.
In general, though, there isn't a way that I know of to bypass the uniform inheritance condition.
I'm using a lot of pairs with types that would normally be coerced freely. However, the notation doesn't explicitly provide the types for the pairs, even though the context does. The following code provides a simple MWE of the principle:
Coercion nat_to_bool (n : nat) : bool :=
match n with
| 0 => false
| _ => true
end.
Inductive newtype : Type :=
| firstconstr : bool -> newtype
| secondconstr : (bool * bool) -> newtype.
Check (3 : bool).
Check (firstconstr 3).
Fail Check (secondconstr (3, true)).
Check (secondconstr (#pair bool bool 3 true)).
The failure message is The term "(3, true)" has type "(nat * bool)%type" while it is expected to have type "(bool * bool)%type". Obviously the type system can figure out the type that it is supposed to be, but can't decide to coerce the value properly.
Is there a way to declare the coercion in such a way that tuples (and other polymorphic data types with inferred types) are coerced properly?
Maybe you can make a notation that desugars to an annotation so the coercion goes through, assuming the pair is always constructed explicitly (that seems fair for a typing judgement G |- x : T where x : T is a pair, although I think the whole judgement is more commonly seen as a triple).
Here's a PoC with the minimal example:
Notation "'secondconstr'' ( x , y )" := (secondconstr (x : bool, y)).
Check (secondconstr' (3, true)).
And in the context of your types judgement, it might look like this:
Notation "Gamma |- v : T" := (types Gamma (v : value, T))
(at level 100).