(** * Basics: Functional Programming in Coq *) (* ################################################################# *) (** * Data and Functions *) (* ================================================================= *) (** ** Enumerated Types *) (** In Coq, we can build practically everything from first principles... *) (* ================================================================= *) (** ** Days of the Week *) (** A datatype definition: *) Inductive day : Type := | monday | tuesday | wednesday | thursday | friday | saturday | sunday. (** A function on days: *) Definition next_weekday (d:day) : day := match d with | monday => tuesday | tuesday => wednesday | wednesday => thursday | thursday => friday | friday => monday | saturday => monday | sunday => monday end. (** Simplification: *) Compute (next_weekday friday). (* ==> monday : day *) Compute (next_weekday (next_weekday saturday)). (* ==> tuesday : day *) (** Second, we can record what we _expect_ the result to be in the form of a Coq example: *) Example test_next_weekday: (next_weekday (next_weekday saturday)) = tuesday. (** We can then present a _proof script_ giving evidence for the claim: *) Proof. simpl. reflexivity. Qed. (** Third, we can ask Coq to _extract_, from our [Definition], a program in a more conventional programming language (OCaml, Scheme, or Haskell) with a high-performance compiler. This facility is very useful, since it gives us a path from proved-correct algorithms written in Gallina to efficient machine code. (Of course, we are trusting the correctness of the OCaml/Haskell/Scheme compiler, and of Coq's extraction facility itself, but this is still a big step forward from the way most software is developed today!) Indeed, this is one of the main uses for which Coq was developed. We'll come back to this topic in later chapters. *) (* ================================================================= *) (** ** Booleans *) (** Another familiar enumerated type: *) Inductive bool : Type := | true | false. (** Booleans are also available in Coq's standard library, but in this course we'll define everything from scratch, just to see how it's done. *) Definition negb (b:bool) : bool := match b with | true => false | false => true end. Definition andb (b1:bool) (b2:bool) : bool := match b1 with | true => b2 | false => false end. Definition orb (b1:bool) (b2:bool) : bool := match b1 with | true => true | false => b2 end. (** Note the syntax for defining multi-argument functions ([andb] and [orb]). *) Example test_orb1: (orb true false) = true. Proof. simpl. reflexivity. Qed. Example test_orb2: (orb false false) = false. Proof. simpl. reflexivity. Qed. Example test_orb3: (orb false true) = true. Proof. simpl. reflexivity. Qed. Example test_orb4: (orb true true) = true. Proof. simpl. reflexivity. Qed. (** We can define new symbolic notations for existing definitions. *) Notation "x && y" := (andb x y). Notation "x || y" := (orb x y). Example test_orb5: false || false || true = true. Proof. simpl. reflexivity. Qed. (** We can also write these function using "if" expressions. *) Definition negb' (b:bool) : bool := if b then false else true. Definition andb' (b1:bool) (b2:bool) : bool := if b1 then b2 else false. Definition orb' (b1:bool) (b2:bool) : bool := if b1 then true else b2. (** **** Exercise: 1 star, standard (nandb) *) Definition nandb (b1:bool) (b2:bool) : bool (* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted. Example test_nandb1: (nandb true false) = true. (* FILL IN HERE *) Admitted. Example test_nandb2: (nandb false false) = true. (* FILL IN HERE *) Admitted. Example test_nandb3: (nandb false true) = true. (* FILL IN HERE *) Admitted. Example test_nandb4: (nandb true true) = false. (* FILL IN HERE *) Admitted. (** [] *) (** Going forward, most exercises will be omitted from the "terse" version of the notes used in lecture. The "full" version contains both longer explanations and all the exercises. *) (* ================================================================= *) (** ** Types *) (** Every expression in Coq has a type describing what sort of thing it computes. The [Check] command asks Coq to print the type of an expression. *) Check true. (* ===> true : bool *) (** If the thing after [Check] is followed by a colon and a type declaration, Coq will verify that the type of the expression matches the given type and halt with an error if not. *) Check true : bool. Check (negb true) : bool. (** Functions like [negb] itself are also data values, just like [true] and [false]. Their types are called _function types_, and they are written with arrows. *) Check negb : bool -> bool. (* ================================================================= *) (** ** New Types from Old *) (** A more interesting type definition: *) Inductive rgb : Type := | red | green | blue. Inductive color : Type := | black | white | primary (p : rgb). (** Let's look at this in a little more detail. An [Inductive] definition does two things: - It defines a set of new _constructors_. E.g., [red], [primary], [true], [false], [monday], etc. are constructors. - It groups them into a new named type, like [bool], [rgb], or [color]. _Constructor expressions_ are formed by applying a constructor to zero or more other constructors or constructor expressions, obeying the declared number and types of the constructor arguments. E.g., these are valid constructor expressions... - [red] - [true] - [primary red] - etc. ...but these are not: - [red primary] - [true red] - [primary (primary red)] - etc. *) (** We can define functions on colors using pattern matching just as we did for [day] and [bool]. *) Definition monochrome (c : color) : bool := match c with | black => true | white => true | primary p => false end. (** Since the [primary] constructor takes an argument, a pattern matching [primary] should include either a variable, as we just did (note that we can choose its name freely), or a constant of appropriate type (as below). *) Definition isred (c : color) : bool := match c with | black => false | white => false | primary red => true | primary _ => false end. (** The pattern "[primary _]" here is shorthand for "the constructor [primary] applied to any [rgb] constructor except [red]." *) (* ================================================================= *) (** ** Modules *) (** [Module] declarations create separate namespaces. *) Module Playground. Definition foo : rgb := blue. End Playground. Definition foo : bool := true. Check Playground.foo : rgb. Check foo : bool. (* ================================================================= *) (** ** Tuples *) Module TuplePlayground. (** A nybble is half a byte -- four bits. *) Inductive bit : Type := | B1 | B0. Inductive nybble : Type := | bits (b0 b1 b2 b3 : bit). Check (bits B1 B0 B1 B0) : nybble. (** We deconstruct a nybble by pattern-matching. *) Definition all_zero (nb : nybble) : bool := match nb with | (bits B0 B0 B0 B0) => true | (bits _ _ _ _) => false end. Compute (all_zero (bits B1 B0 B1 B0)). (* ===> false : bool *) Compute (all_zero (bits B0 B0 B0 B0)). (* ===> true : bool *) End TuplePlayground. (* ================================================================= *) (** ** Numbers *) Module NatPlayground. (** There are many possible representations of natural numbers. You may be familiar with decimal, hexadecimal, octal, and binary. For simplicity in proofs, we choose unary: [O] represents zero, and [S] represents adding an additional unary digit. That is, [S] is the "successor" operation, which, when applied to the representation of n, gives the representation of n+1. *) Inductive nat : Type := | O | S (n : nat). (** With this definition, 0 is represented by [O], 1 by [S O], 2 by [S (S O)], and so on. *) (** Again, let's look at this a bit more closely. The definition of [nat] says how expressions in the set [nat] can be built: - the constructor expression [O] belongs to the set [nat]; - if [n] is a constructor expression belonging to the set [nat], then [S n] is also a constructor expression belonging to the set [nat]; and - constructor expressions formed in these two ways are the only ones belonging to the set [nat]. *) (** Critical point: this just defines a _representation_ of numbers -- a unary notation for writing them down. The names [O] and [S] are arbitrary. They are just two different "marks", with no intrinsic meaning. We could just as well represent numbers with different marks: *) Inductive otherNat : Type := | stop | tick (foo : otherNat). (** The _interpretation_ of these marks arises from how we use them to compute. *) Definition pred (n : nat) : nat := match n with | O => O | S n' => n' end. End NatPlayground. (** As a convenience, standard decimal numerals can be used as a shorthand for sequences of applications of [S] to [O]; Coq uses the same shorthand when printing: *) Check (S (S (S (S O)))). (* ===> 4 : nat *) Definition minustwo (n : nat) : nat := match n with | O => O | S O => O | S (S n') => n' end. Compute (minustwo 4). (* ===> 2 : nat *) (** Recursive functions are introduced using the [Fixpoint] keyword. *) Fixpoint even (n:nat) : bool := match n with | O => true | S O => false | S (S n') => even n' end. (** We could define [odd] by a similar [Fixpoint] declaration, but here is a simpler way: *) Definition odd (n:nat) : bool := negb (even n). Example test_odd1: odd 1 = true. Proof. simpl. reflexivity. Qed. Example test_odd2: odd 4 = false. Proof. simpl. reflexivity. Qed. (** A multi-argument recursive function. *) Module NatPlayground2. Fixpoint plus (n : nat) (m : nat) : nat := match n with | O => m | S n' => S (plus n' m) end. Compute (plus 3 2). (* ===> 5 : nat *) (* [plus 3 2] i.e. [plus (S (S (S O))) (S (S O))] ==> [S (plus (S (S O)) (S (S O)))] by the second clause of the [match] ==> [S (S (plus (S O) (S (S O))))] by the second clause of the [match] ==> [S (S (S (plus O (S (S O)))))] by the second clause of the [match] ==> [S (S (S (S (S O))))] by the first clause of the [match] i.e. [5] *) (** Another: *) Fixpoint mult (n m : nat) : nat := match n with | O => O | S n' => plus m (mult n' m) end. Example test_mult1: (mult 3 3) = 9. Proof. simpl. reflexivity. Qed. (** Pattern-matching two values at the same time: *) Fixpoint minus (n m:nat) : nat := match n, m with | O , _ => O | S _ , O => n | S n', S m' => minus n' m' end. End NatPlayground2. (** Again, we can make numerical expressions easier to read and write by introducing notations for addition, subtraction, and multiplication. *) Notation "x + y" := (plus x y) (at level 50, left associativity) : nat_scope. Notation "x - y" := (minus x y) (at level 50, left associativity) : nat_scope. Notation "x * y" := (mult x y) (at level 40, left associativity) : nat_scope. Check ((0 + 1) + 1) : nat. (** When we say that Coq comes with almost nothing built-in, we really mean it: even testing equality is a user-defined operation! Here is a function [eqb], which tests natural numbers for [eq]uality, yielding a [b]oolean. Note the use of nested [match]es (we could also have used a simultaneous match, as in [minus].) *) Fixpoint eqb (n m : nat) : bool := match n with | O => match m with | O => true | S m' => false end | S n' => match m with | O => false | S m' => eqb n' m' end end. (** Similarly, the [leb] function tests whether its first argument is less than or equal to its second argument, yielding a boolean. *) Fixpoint leb (n m : nat) : bool := match n with | O => true | S n' => match m with | O => false | S m' => leb n' m' end end. Example test_leb1: leb 2 2 = true. Proof. simpl. reflexivity. Qed. Example test_leb2: leb 2 4 = true. Proof. simpl. reflexivity. Qed. Example test_leb3: leb 4 2 = false. Proof. simpl. reflexivity. Qed. (** We'll be using these (especially [eqb]) a lot, so let's give them infix notations. *) Notation "x =? y" := (eqb x y) (at level 70) : nat_scope. Notation "x <=? y" := (leb x y) (at level 70) : nat_scope. Example test_leb3': (4 <=? 2) = false. Proof. simpl. reflexivity. Qed. (** We have two notions of equality: - [=] is a logical _claim_ that we can attempt to _prove_. - [=?] is a boolean _expression_ up and that Coq _computes_. *) (* ################################################################# *) (** * Proof by Simplification *) (** A general property of natural numbers: *) Theorem plus_O_n : forall n : nat, 0 + n = n. Proof. intros n. simpl. reflexivity. Qed. (** The [simpl] tactic is actually redundant, as [reflexivity] already does some simplification for us: *) Theorem plus_O_n' : forall n : nat, 0 + n = n. Proof. intros n. reflexivity. Qed. (** Any other (fresh) identifier could be used instead of [n]: *) Theorem plus_O_n'' : forall n : nat, 0 + n = n. Proof. intros m. reflexivity. Qed. (** Other similar theorems can be proved with the same pattern. *) Theorem plus_1_l : forall n:nat, 1 + n = S n. Proof. intros n. reflexivity. Qed. Theorem mult_0_l : forall n:nat, 0 * n = 0. Proof. intros n. reflexivity. Qed. (* ################################################################# *) (** * Proof by Rewriting *) (** A (slightly) more interesting theorem: *) Theorem plus_id_example : forall n m:nat, n = m -> n + n = m + m. Proof. (* move both quantifiers into the context: *) intros n m. (* move the hypothesis into the context: *) intros H. (* rewrite the goal using the hypothesis: *) rewrite -> H. reflexivity. Qed. (** The uses of [intros] name the hypotheses as they are moved to the context. The [rewrite] needs to know which equality is being used -- and in which direction -- to do the replacement. *) (** The [Check] command can also be used to examine the statements of previously declared lemmas and theorems. The two examples below are lemmas about multiplication that are proved in the standard library. (We will see how to prove them ourselves in the next chapter.) *) Check mult_n_O. (* ===> forall n : nat, 0 = n * 0 *) Check mult_n_Sm. (* ===> forall n m : nat, n * m + n = n * S m *) (** We can use the [rewrite] tactic with a previously proved theorem instead of a hypothesis from the context. If the statement of the previously proved theorem involves quantified variables, as in the example below, Coq will try to fill in appropriate values for them by matching the body of the previous theorem statement against the current goal. *) Theorem mult_n_0_m_0 : forall p q : nat, (p * 0) + (q * 0) = 0. Proof. intros p q. rewrite <- mult_n_O. rewrite <- mult_n_O. reflexivity. Qed. (* ################################################################# *) (** * Proof by Case Analysis *) (** Sometimes simple calculation and rewriting are not enough... *) Theorem plus_1_neq_0_firsttry : forall n : nat, (n + 1) =? 0 = false. Proof. intros n. simpl. (* does nothing! *) Abort. (** We can use [destruct] to perform case analysis: *) Theorem plus_1_neq_0 : forall n : nat, (n + 1) =? 0 = false. Proof. intros n. destruct n as [| n'] eqn:E. - reflexivity. - reflexivity. Qed. (** Note the "bullets" marking the proofs of the two subgoals. *) (** Another example (using booleans): *) Theorem negb_involutive : forall b : bool, negb (negb b) = b. Proof. intros b. destruct b eqn:E. - reflexivity. - reflexivity. Qed. (** We can have nested subgoals (and we use different "bullets" to mark the inner ones): *) Theorem andb_commutative : forall b c, andb b c = andb c b. Proof. intros b c. destruct b eqn:Eb. - destruct c eqn:Ec. + reflexivity. + reflexivity. - destruct c eqn:Ec. + reflexivity. + reflexivity. Qed. (** Besides [-] and [+], we can use [*] (asterisk) or any repetition of a bullet symbol (e.g. [--] or [***]) as a bullet. We can also enclose sub-proofs in curly braces: *) Theorem andb_commutative' : forall b c, andb b c = andb c b. Proof. intros b c. destruct b eqn:Eb. { destruct c eqn:Ec. { reflexivity. } { reflexivity. } } { destruct c eqn:Ec. { reflexivity. } { reflexivity. } } Qed. (** One final convenience: Instead of intros x y. destruct y as [|y] eqn:E. we can write intros x [|y]. *) Theorem plus_1_neq_0' : forall n : nat, (n + 1) =? 0 = false. Proof. intros [|n]. - reflexivity. - reflexivity. Qed. (** If there are no constructor arguments that need names, we can just write [[]] to get the case analysis. *) Theorem andb_commutative'' : forall b c, andb b c = andb c b. Proof. intros [] []. - reflexivity. - reflexivity. - reflexivity. - reflexivity. Qed. (* ################################################################# *) (** * Testing Your Solutions *) (** You can run [make BasicsTest.vo] to check your solution for common errors: - Deleting or renaming exercises. - Changing what you were supposed to prove. - Leaving the exercise unfinished. *)