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.