In the "Typing rules" section of the coq reference manual, a note says "We may have π πΎπ π₯:=π‘:π ππ π’ well-typed without having ((Ξ»π₯:π. π’) π‘) well-typed (where π is a type of π‘). This is because the value π‘ associated with π₯ may be used in a conversion rule (see Section Conversion rules)."
There is a reduction rule that replaces a let-bound variable with its definition (this is called delta-reduction in the Conversion Rules section of the manual).
So for example, if you have an expression let n := 3 in body
, and somewhere in body
you had an place where an expression of type "vector of length 3" was required, but you wrote an expression of type "vector of length n", then the type checker will automatically unfold the definition of n
. On the other hand, the body of a lambda-expression is type-checked independently, so if you have (fun n => body) 3
, then the type checker will leave n
unexpanded.
Here's a code snippet illustrating the difference:
Require Vector.
Section example.
Variable A : Type.
Variable f : Vector.t A 3 -> unit.
Check
let n := 3 in
(fun (x: Vector.t A n) => f x). (* This type checks. *)
Fail Check
(fun n =>
(fun (x: Vector.t A n) => f x)) (* This gives a type error. *)
3 .
End example.