I am very new to Coq. I am trying to experiment with Coq's dependent types. What I want to do, is to simply feed an even number to a function. For example, in pseudo-code:
def next_even(n: {n: Integer | n is even}) :=
{
add n 2
}
Then I want to utilize the function with different arguments, such as (in pseudo-code):
next_even(1) // Coq should show an error
next_even(4) // Coq should compute 6
So, in Coq, I want to do the following:
Compute (next_even 1).
Compute (next_even 4).
How can I construct it?
Here is a direct translation of your function in Coq:
Note that you need the
proj1_sigfunction to extractnfrom the subset type{n : nat | Nat.even n = true}. To useadd_two_even, you also need to go the other way around: go from a number to an element of{n | Nat.even n = true}. In Coq, this requires manual proof. For concrete values ofn, this is easy:The
existconstructor wraps a valuexwith a proof ofP x, producing an element of the subset type{x | P x}. Theeq_reflterm is a proof ofx = xfor any value ofx. Whennis a concrete number, Coq can evaluateNat.even nand find ifeq_reflis a valid proof ofNat.even n = true. Whennis1,Nat.even n = false, and the check fails, leading to the first error message. Whennis4, the check succeeds.Things get more complicated when
nis not a constant, but an arbitrary expression. A proof thatNat.even n = truecan require detailed reasoning that must be guided by the user. For instance, we know thatNat.even (n + n) = truefor any value ofn, but Coq does not. Thus, to calladd_two_evenon something of the formn + n, we need to show a lemma.There are some tools for facilitating this style of programming, such as the equations plugin, but the general wisdom in the Coq community is that you should avoid subset types for functions like
add_two_even, where the constraints do not substantially simplify the properties about the function.You can find many examples of good uses of subset types in the Mathematical Components libraries. For example, the libraries use subset types to define a type
n.-tuple Tof lists of lengthn, and a type'I_nof integers bounded byn. This allows us to define a total accessor functiontnth (t : n.-tuple T) (i : 'I_n) : Tthat extracts theith element of the listt. If we define this accessor for arbitrary lists and integers, we need to pass a default value to the function to return when the index is out of bounds, or change the function's signature so that it returns a value of typeoption Tinstead, indicating that the function can fail to return a value. This change makes properties of the accessor function harder to state. For example, consider theeq_from_tnthlemma, which says that two lists are equal if all their elements are equal:The statement of this lemma for arbitrary lists becomes more complicated, because we need an extra assumption saying that the two lists are of the same size. (Here,
x0is the default value.)