Programming and Proving with Total Functions
The core design philosophy of F* is that the type of a term (a program
fragment) is a specification of its runtime behavior. We write e :
t
to mean that a term e
has type t
. Many terms can have the
same type and the same term can have many types.
One (naive but useful) mental model is to think of a type as
describing a set of values. For instance, the type int
describes
the set of terms which compute integer results, i.e., when you have
e : int
, then when e
is reduced fully it produces a value in
the set {..., -2, -1, 0, 1, 2, ...}
. Similarly, the type bool
is the type of terms that compute or evaluate to one of the values in
the set {true,false}
. Unlike many other languages, F* allows
defining types that describe arbitrary sets of values, e.g., the type
that contains only the number 17
, or the type of functions that
factor a number into its primes.
When proving a program e
correct, one starts by specifying the
properties one is interested in as a type t
and then trying to
convince F* that e
has type t
, i.e., deriving e : t
.
The idea of using a type to specify properties of a program has deep
roots in the connections between logic and computation. You may find
it interesting to read about propositions as types,
a concept with many deep mathematical and philosophical
implications. For now, it suffices to think of a type t
as a
specification, or a statement of a theorem, and e : t
as
computer-checkable claim that the term e
is a proof of the theorem
t
.
In the next few chapters we’ll learn about how to program total functions and prove them correct.