I'm trying to define a simple stack-based language in Coq. For now, the instruction set contains push which pushes a nat, and an instruction pop which pops one. The idea is that programs are dependently typed; Prog 2 is a program which leaves two elements on the stack after execution.
This is implemented by this simple program:
Require Import Coq.Vectors.VectorDef.
Inductive Prog : nat -> Type :=
| push : forall n : nat, nat -> Prog n -> Prog (S n)
| pop : forall n : nat, Prog (S n) -> Prog n.
Fixpoint eval (n : nat) (p : Prog n) : t nat n :=
match p with
| push _ n p => cons _ n _ (eval _ p)
| pop _ p => match eval _ p with
| cons _ _ _ stack => stack
end
end.
I now want to add an instruction pop' which pops an element of the stack but can only be applied when there are at least two elements on the stack.
Inductive Prog : nat -> Type :=
| push : forall n : nat, nat -> Prog n -> Prog (S n)
| pop' : forall n : nat, Prog (S (S n)) -> Prog (S n).
When using the same Fixpoint as above (changing pop to pop') I get the error
The term "stack" has type "t nat n0" while it is expected to have type "t nat (S k)".
So I thought I could do this with Program. So I use:
Require Import Coq.Program.Tactics Coq.Logic.JMeq.
Program Fixpoint eval (n : nat) (p : Prog n) : t nat n :=
match p with
| push _ n p => cons _ n _ (eval _ p)
| pop' k p => match eval _ p with
| cons _ l _ stack => stack
| nil _ => _
end
end.
However, for some reason this generates weird obligations, which I don't think can be solved. The first (of two) obligations left after automatic attempts is:
k : nat
p0 : Prog (S k)
p : Prog (S (S k))
Heq_p : JMeq (pop' k p) p0
l, n0 : nat
stack : t nat n0
h : nat
t : t nat n0
Heq_anonymous0 : JMeq (cons nat l n0 stack) (cons nat h n0 t)
______________________________________(1/1)
n0 = S k
I don't see a way to link the k, which is the type argument for Prog, and the n0, which is the type argument for the vector type t.
Why does this Program yield this weird obligation, and how can I write the evaluation function circumventing this issue?
Before answering your question, note it is impossible to write any program in your language! (It does not have any effect on the issue you are describing, but it is still worth pointing it out anyway...)
From Coq Require Import Vectors.Vector.
Set Implicit Arguments.
Inductive Prog : nat -> Type :=
| push : forall n : nat, nat -> Prog n -> Prog (S n)
| pop' : forall n : nat, Prog (S (S n)) -> Prog (S n).
Fixpoint not_Prog n (p : Prog n) : False :=
match p with
| push _ p' => not_Prog p'
| pop' p' => not_Prog p'
end.
Now, to your question. It is because of this and related issues that many people prefer to avoid this style of programming in Coq. In this case, I find it easier to program your function using tl, which extracts the tail of a vector.
From Coq Require Import Vectors.Vector.
Set Implicit Arguments.
Inductive Prog : nat -> Type :=
| empty : Prog 0
| push : forall n : nat, nat -> Prog n -> Prog (S n)
| pop' : forall n : nat, Prog (S (S n)) -> Prog (S n).
Fixpoint eval (n : nat) (p : Prog n) : t nat n :=
match p with
| empty => nil _
| push n p => cons _ n _ (eval p)
| pop' p => tl (eval p)
end.
If you are still interested in working with this kind of datatype in Coq, you might want to have a look at the Equations plugin, which provides better support for dependent pattern matching.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With