Inductive sets Induction is an important concept in the theory of programming language -- it occurs when defining language syntax, as well as when specifying the execution of repetitive constructs (e.g., loops, recursion). An inductively defined set A is a set that is built using a set of axioms and inductive (inference) rules. Axioms of the form: ------ a in A specify elements that are by default in the set A. And inductive rules: a1 in A .. an in A ------------------ a in A indicate that a is an element of A, provided that a1, .., an are elements of A. The set A is the set of all elements that can be inferred to belong to A using a (finite) number of applications of these rules, starting only from axioms. In other words, for each element a of A, we must be able to construct a finite proof tree whose final conclusion is "a in A". Example 1. The language of a grammar is an inductive set. For instance, the set of arithmetic expressions can be described with 2 axioms, and 2 inductive rules: --------- -------- x in Expr n in Expr e1 in Expr e2 in Expr --------------------- e1+e2 in Expr e1 in Expr e2 in Expr --------------------- e1*e2 in Expr Example 2: Non-negative integers can be inductively defined: -------------- 0 in NonNegInt n in NonNegInt -------------------- succ(n) in NonNegInt Example 3: The one-step evaluation relation -> (regarded as a subset of Config x Config) is an inductively defined set. The definition of this set is given by the semantic rules. Example 4: The transitive, reflexive closure ->* (i.e., the multi-step evaluation relation) can be inductively defined: --------- mc ->* mc mc -> mc'' mc'' ->* mc' ----------------------- mc ->* mc' Inductive proofs We can prove facts about elements of an inductive set using an inductive reasoning that follows the structure of the set definition. This is called "rule induction". To prove that property P holds for all elements of A: forall a in A: P(a) we need to prove: ------ 1. (base cases) P(a) holds for all axioms a in A 2. (inductive cases) P(a1) and .. and P(an) implies P(a) for all a1 in A .. an in A rules: ------------------ a in A If A describe a syntactic set, we refer to rule induction as "structural induction". And if A is the set of non-negative integers, rule induction becomes "mathematical induction". Intro to large-step semantics So far we have defined the small step evaluation relation ->, and then took its transitive and reflexive closure ->* to describe the execution of multiple steps of evaluation. In particular, if mc is some start configuration, and fc is a final configuration, the evaluation mc ->* fc shows the result of the computation of mc. It turns out that there is an alternative way to specify the operational semantics of a language in such a way that it would directly give the final result. This alternative method is called "large-step semantics" (as opposed to the one-step evaluation relation, which provides a "small-step semantics). We'll use the same configurations as before, but define a large step evaluation relation: Eval subset of Expr x Store x Int x Store and will write => (where "=>" is a downarrow) to mean that (e,s,n, s') in Eval. In other words, e in store s evaluates in one big step directly to n, and final store s'. The large step semantics boils down to defining the relation =>. We can do that inductively: ------------- (int) => ----------------- (var) => => => (plus)---------------------------------------- where n = n1+n2 => => => (mul)------------------------------------- where n = n1*n2 => => n1]> => (asg)---------------------------------------------- where n = n1*n2 => To see how we use these rules, take an example: evaluate (y+2)*(x+1) in state s ={x=4,y=3}. We get the following proof tree for the fact that <(y+2)*(x+1), s> => 24: -------------- -------------- -------------- -------------- => <4,s> <2,s> => <2,s> => <3,s> <1,s> => <1,s> ------------------------------ ------------------------------ => <6,s> => <4,s> ------------------------------------------------------------------ <(y+2)*(x+1), s> => 24 A closer look to this structure reveals the relation between small step and large-step evaluation: a depth-first traversal of the large-step proof tree yields the sequence of one-step transitions in small-step evaluation. Equivalence of semantics So far, we have specified the semantics of our language of arithmetic expressions using two different sets of rules: small-step and large-step. Are they expressing the same meaning of arithmetic expressions? Can we show that they express the same thing? [Equivalence of semantics] For all expressions e, stores s, and integers n, we have: ->* iff => Proof sketch. "<=" Want to prove that property: P(e) = "forall s, s', n: => implies ->* holds for all e in Expr. We can use structural induction on expressions e and examine the following cases. Case e = x. If => , then, by looking at the large-step rules, we see that only one rule matches, and that rule requires n = s(x). Then, -> also holds, using a small-step axiom. We conclude that ->* holds. Case e = n. In this case, ->* trivially holds because of reflexivity of ->*. Case e = e1+e2. This is an inductive case. We want to prove that, if P(e1) and P(e2) hold: P(e1) = forall n,s,s': => implies ->* P(e2) = forall n,s,s': => implies ->* then P(e) also holds: P(e) = forall n,s,s': => implies ->* Let's start with the premise in P(e): => . By inspecting the large-step semantic rules, we see that only one rule applies, and that it must be the case that => , => for some n1 and n2 such that n = n1+n2. We can now apply the inductive hypothesis P(e1) and P(e2) to conclude that: ->* and ->* . From here, we can use the Lemma 1 below to determine that: ->* ->* -> which proves this case. Case e = e1*e2, similar to the case above. Lemma 1. If ->* then ->* and ->* . Proof. By (mathematical) induction on the number of evaluation steps in ->*. "=>". This implication follows from the Lemma 2 below. Lemma 2. For all e, e', s, n, if -> and => , then => .