Start of topic | Skip to actions

Programming in Coq and Comparisons to Concoqtion

Scribe: Joseph Young

Let us begin with a brief disclaimer. We are not advocating Coq as a general purpose programming language. In fact, Coq is missing several important features that allow its use as such a language. However, Coq possesses a very powerful type system. Once we understand how to program in Coq and use this type system, we will have a better understanding with how Coq actually proves theorems. Further, Concoqtion uses Coq to enhance the type system of OCaml for use as a general purpose programming language. Thus, understanding the programming features of Coq will help us further understand many features of Concoqtion.

Our first example will allow us to define a sized list in Coq. Recall, a sized list is a list where the size of the list is built in as a type. The definition in Coq is given by

Inductive SList (A:Set) : nat -> Set :=
| Nil : SList A 0
| Cons : forall (m:nat), (A -> (SList A m) -> (SList A (S m))).
Compare this to the definition within Concoqtion:
type ('n:'(nat),'a) slist =
| Nil : ('(0),'a) slist
| Cons of let 'm:'(nat) in 'a * ('(m),'a) slist : ('(1+m),'a) slist
For later reference, when creating an inductive definition within Coq, arguments to the left hand side of the colon are called parameters while arguments to the right hand of the colon are called indexes. In other words, SList works like a function with type (A:set)->nat->Set. In this case, A has been declared as a paramter while nat has been declared as an index.

We can create a sized list in Coq with the following code

Definition my_list:=Cons nat 1 34 (Cons nat 0 17 (Nil nat)).
This is a bit more verbose than Concoqtion where we can define a similar list with
let x=Cons .|'(1)| (34,(Cons .|'(0)| (17,Nil));;
We can make the syntax on Coq a little more concise if we declare A to be an implicit paramter check this.

Next, we will declare a function that accepts a sized list of naturals and sums them together

Fixpoint sum_list (n:nat) (l:(SList nat n)) {struct l} : nat :=
    match l with
    | Nil => 0
    | Cons m x xs => x + (sum_list m xs)
end.
This illustrates a number of key concepts. First, in Coq recursive functions are defined by Inductive. Coq does not support general recursion since it needs to guarantee all functions terminate. Thus, we added {struct l} to the prototype. This tells Coq that the recursive call will be within a substructure of l. In other words, we are going to recursively tear apart our list until we come to the end. Second, notice that Cons within the match statement only accepts three arguments instead of four. We have three since A is defined as a parameter, not an index, in the definition of SList. In other words, we can not manipulate parameters within the data structure. They are immutable once declared. We test this function with the following commands
Coq < Definition my_list:=Cons nat 1 34 (Cons nat 0 17 (Nil nat)).
my_list is defined

Coq < Print my_list.
my_list = Cons nat 1 34 (Cons nat 0 17 (Nil nat))
     : SList nat 2


Coq < Definition my_sum:=sum_list 2 my_list.
my_sum is defined

Coq < Print my_sum.
my_sum = sum_list 2 my_list
     : nat


Coq < Eval compute in my_sum.
     = 51
     : nat

Notice that Coq does not automatically evaluate each statement. In order to force computation of my_sum, we needed to invoke a tactic by calling Eval compute. We can define a similar summation function within Concoqtion with the following code
let rec sum_list .|n:'(nat)| (l:('(n),int) slist) : int =
    match l as ('m:'(nat),int) slist in int with
    | Nil -> 0
    | Cons .|m:'(nat)| (x,xs) -> x+sum_list .|'(m)| xs
;;
We test it with the following commands
# let x=Cons .|'(1)| (34,(Cons .|'(0)| (17,Nil)));;
val x :
  ('(S
       ((fix plus (n m : nat) {struct n} : nat :=
           match n with
           | O => m
           | S p => S (plus p m)
           end) 0 1)),
   int)
  slist = Cons (34, Cons (17, Nil))
# let _ =sum_list .|'(2)| x;;
- : int = 51

We now define the map function for sized lists. As its input it will accept a function and a sized list. Then, it will apply this function to each argument in the above list. In Coq, we define the function with the following

Fixpoint my_map (A:Set) (n:nat) (f:A->A) (l:(SList A n)) {struct l}:(SList A n):=
    match l in (SList _ n) return (SList _ n) with
    | Nil => Nil A
    | Cons n x xs => Cons A n (f x) (my_map A n f xs)
end.
The match statement contains two pieces of information annoted by "in" and "return". The keyword "in" tells the match statement that the argument, l, will have a particular type. They keyword "return" specifies the return type for the entire match statement. We need both of these annotations since Coq can not correctly infer this information. For example, the following definition
Fixpoint my_map2 (A:Set) (n:nat) (f:A->A) (l:(SList A n)) {struct l}:(SList A n):=
    match l return (SList _ n) with
    | Nil => Nil A
    | Cons n x xs => Cons A n (f x) (my_map A n f xs)
end.
generates the error
Error:
In environment
my_map2 : forall (A : Set) (n : nat), (A -> A) -> SList A n -> SList A n
A : Set
n : nat
f : A -> A
l : SList A n
The term "Nil A" has type "SList A 0" while it is expected to have type
 "SList A n"
We can define the map function in Concoqtion with the following code
let rec my_map .|m:'(nat)| (f:'a->'b) (l:('(m),'a) slist):('(m),'a) slist=
    match l in ('(m),'a) slist with
    | Nil -> Nil
    | Cons .|i:'(nat)| (x,xs) ->
        Cons .|'(i)| ((f x),(my_map .|'(i)| f xs))
Similar to Coq, Concoqtion can not always correctly infer the correct types for the match statement. Thus, it contains the keyword "as" to denote the type of the argument and "in" to denote the type of the entire match statement. These keywords function exactly the same as "in" and "return" respectively in Coq.

-- JosephYoung - 02 Feb 2007


End of topic
Skip to actions | Back to top
Creative Commons LicenseThis work is licensed under a Creative Commons Attribution 2.5 License. Please follow our citation guidelines.