I’m reading through CPDT and there is one example that I’m not understanding:
Definition plus_rec : nat -> nat -> nat :=
nat_rec (fun _ : nat => nat -> nat) (fun m => m) (fun _ r m => S (r m)).
Most of this makes sense except for the fun _ : nat => nat -> nat. I don’t understand how this can have any meaning at all, this function appears to be returning a type with no value.
Am I misunderstanding something fundamental here about Coq? What is the significance of that lambda?
Let’s take a look at
nat_rec:Its first argument is a
Pwhich, given anat, returns aSet. Then you are going to give an element of the setP O, and an element of the setP (S n)assuming you have an element fromP n, and finally with all that, you are given back something of typeforall n, P nwhich means it can construct an element of typeP nfor everynyou pass in as input (if you pass O, it’s going to give you back theP Oyou provided, otherwise it’s going to build your result by repeated applications of the third argument).Now, that lambda which is confusing you is that
PI’ve been talking about. It is given anatand from it builds the “return type”. In particular we are trying to have the return type ofnat_rec, namelyforall n : nat, P nbe the type ofplus_rec, that isnat -> nat -> nat. This can be done by instantiatingPsuch that for all n,P n = nat -> nat. Which is what we obtain using the lambda-termfun _ : nat => nat -> nat.This term ignores the input
nat(becausenat_recis general enough to build things whose type depend on the firstnatthey receive, but the type ofplus_recpartially applied to any first argument does not actually depend on the value of that argument). Then it just returnsnat -> nat(which is the type of the partial application of addition to one operand: we still expect anatbefore we can return the sumnat).Now if you take a look at
nat_recpartially applied to that funny lambda-term, the type is:(I just replied
Pwith(fun _ : nat => nat -> nat). Now, this simplifies into:Now this should definitely be confusing.
You should think of the first argument you need to give as the type of the partial application of
plustoO. It receives the second operand, and returns the sum of the two:The second argument is given a
(n : nat), a functionr : nat -> nat, anotherm : nat, and needs to return a finalnat. It is the most confusing in the bunch. Morally,ris aP n, that is, given our goal, “a function who knows how to add n to its input”. Morally again, the last(nat -> nat)should be aP (S n), that is “a function who knows how to add (S n) to its input”. In particular, we named this inputm. You should be able to convince yourself that the result should be:With all this, we managed to build the function
plus.Note that is a very error-prone way to define
plus_rec, in particular because you have to remember really hard what is what or you’re not going to define the function you want, because the typing is really loose (see how we ignore thenmost of the time). In fact, there was a bug in an earlier draft of that same function, which did type-check.I believe this is shown as to show how everything is under the rug, but do not think that this is the way you should define plus_rec in general.
Finally, this is quite hard to explain and I am pretty sure my answer will not be crystal-clear. Feel free to drop comments and I’ll edit the answer to make things more clean.