How does Coq ensure that the request completes in finite time?
How does Coq ensure that the request completes in finite time?
For example, a function F to find the item X in the the list L might look like this:
1. If L is empty, return false.
2. If the front of L is X, return true.
3. Otherwise return the result of calling F on L with its front item removed.
The list must get smaller every recursive call so there's a limit to the recursion. Lots of functions recurse in this way (e.g. map, filter, reduce, max) and Coq can detect these automatically usually.
For more complicated cases, you can provide some numeric metric to Coq and write a proof that this metric must eventually reach some limit. For example, to prove quicksort terminates, you usually show the length of the lists called in the recursive calls are smaller each time.
So, in general, there's no procedure to show an arbitrary function terminates, but for the kind of functions people usually write, there are practical ways to prove termination.
Coq's correctness depends on termination. In Coq the propositions are types. To prove a proposition is to construct a program of this type. If one allowed non-termination, then it would be easy to construct a program of any type (like in OCaml let rec f() = f() has type () -> 'a), so you could prove anything.
In this context, it's isn't very illuminating to tell a non-Coq user that all Coq functions terminate; formulating your function definition into something that Coq will accept is the difficult part.
[I guess that's what you're saying, but there seems to be some confusion here, so it's perhaps best to try to be explicit.]
http://www.cse.chalmers.se/research/group/logic/TypesSS05/Ex...
I know we can't execute an unbounded number of steps during compilation, but how does this work with program extraction? Can we extract a Haskell/Scheme/etc. program which implements our unbounded, corecursive step function?
CoInductive Stream (T: Type): Type :=
Cons: T -> Stream T -> Stream T.
Fixpoint fac(n : nat): nat :=
match n with
| 0 => 1
| S n => n * (fac n)
end.
CoFixpoint facs : nat -> Stream nat :=
fun n =>
Cons (fac n) (facs (n+1)).
Definition fs := facs 0.
It's extracted to the following Haskell code: data Stream t =
Cons t (Stream t)
fac :: Nat -> Nat
fac n =
case n of {
O -> S O;
S n0 -> mul n0 (fac n0)}
facs :: Nat -> Stream Nat
facs n =
Cons (fac n) (facs (add n (S O)))
fs :: Stream Nat
fs = facs O
It should be possible to define a function that given a Turing Machine returns the stream of its states analogously. But to actually compute the states in Haskell you would have to write a driver function that actually forces their computation one after the other.Even without corecursive data, you can define a function that for a given TM returns a function of type nat->state, which computes the state of the TM after k steps. In a way this value also represents the whole, possibly infinite, computation of the machine.
"corecursion" is usually a recursion that goes via multiple definitions.
Not that I don't agree with you but I find it a common failure mode of HDs to just never reply to things. I guess it's also a common failure mode of network systems too.
Can Coq detect that you are enforcing I/O timeouts in a way that guarantees finite time?
More generally, Coq is "just" a well integrated proof tool & I can't see any reason why you couldn't include such features but at some point you have to draw the line and be explicit about what it is that you're actually proving: if your proof assumes data store responsiveness then it's OK for it to do that so long as you're explicit about the resulting limitations IMO. The goal of ever increasing model fidelity is a rabbit hole from which the programmer/prover might never return otherwise :)
In practice, we should use a timeout in the implementation of all the external calls, but we did not and our model to not enforce it.