Formal Semantics Aspects to formalize Syntax : what’s a syntactically well-formed program? Why formalize? • some language features are tricky, • formalize by a context-free grammar, e.g. in EBNF notation e.g. generalizable type variables, nested functions Static semantics : • some features have subtle interactions, which syntactically well-formed programs are also e.g. polymorphism and mutable references semantically well-formed? • some aspects often overlooked in informal descriptions, e.g. evaluation order, handling of errors • i.e., name resolution, type checking, etc. • formalize using typing rules, well-formedness judgments Want a clear and unambiguous specification that can be used by language designers and language implementors Dynamic semantics : (and programmers when necessary) to what does a semantically well-formed program evaluate? • i.e., run-time behavior of a type-correct program Ideally, would allow rigorous proof of • formalize using operational, denotation, and/or axiomatic • desired language properties, e.g. safety semantics rules • correctness of implementation techniques Metatheory : what are the properties of the formalization itself? • e.g., is static semantics sound w.r.t. dynamic semantics? Craig Chambers 164 CSE 505 Craig Chambers 165 CSE 505 Approach Lambda calculus Formalizing & proving properties about a full language The tiniest core of a functional programming language is very hard, very tedious • Alonzo Church, 1930s • many, many cases to consider • lots of interacting features The foundation for all formal study of programming languages Better approach: boil full-sized language down into its essential core, then Outline of study: formalize and study the core • untyped λ -calculus: • cut out much of the complication as possible, syntax, dynamic semantics, properties without losing the key parts that need formal study • simply typed λ -calculus: • hope that insights gained about the core also static semantics, soundness carry over to the full language • standard extensions to λ -calculus: syntax, dynamic semantics, static semantics • polymorphic λ -calculus: Can study language features in stages: syntax, dynamic semantics, static semantics • a very tiny core • then extend with an additional feature • then extend again (or separately) Craig Chambers 166 CSE 505 Craig Chambers 167 CSE 505
Untyped λ -calculus: syntax Free and bound variables λ I . E binds I in E Syntax: function / abstraction E ::= λ I . E call / application | E E An occurrence of a variable I is free in an expression E variable | I if it’s not bound by some enclosing lambda in E [That’s it!] FV ( E ): set of free variables in E Application binds tighter than . FV ( I ) = { I } Can freely parenthesize as needed FV ( λ I . E ) = FV ( E ) - { I } FV ( E 1 E 2 ) = FV ( E 1 ) ∪ FV ( E 2 ) Example (with minimum parens): ( λ x. λ y. x y) λ z.z FV ( E ) = ∅ ⇔ E is closed ML analogue (if ignore types): ( fn x => ( fn y => x y)) ( fn z => z) Trees described by this grammar are called term trees Craig Chambers 168 CSE 505 Craig Chambers 169 CSE 505 α -renaming Evaluation, β -reduction First semantic property of λ -calculus: Define how a λ -calculus program “runs” via a set of rewrite rules, a.k.a. reductions a bound variable in a term tree (and all its references) can be renamed without affecting the semantics of the term tree • “ E 1 → E 2 ” means “ E 1 reduces to E 2 in one step” • cannot rename free variables One rule: ( λ I . E 1 ) E 2 → [ E 2 / I ] E 1 Precise definition: • “applying a function to an argument expression α -equivalence: λ I 1 . E ⇔ λ I 2 .[ I 2 / I 1 ] E (if I 2 ∉ FV ( E )) reduces to the function’s body after substituting the argument expression for the function’s formal” • this rule is called the β -reduction rule [ E 2 / I ] E 1 : substitute all free occurrences of I in E 1 with E 2 • (formalized soon) Other rules state that the β -reduction rule can be applied to nested subexpressions, too Since names of bound variables “don’t matter”, it’s convenient • (formalized later) to treat all α -equivalent term trees as a single term • define all later semantics for terms Define how a λ -calculus program “runs” to compute a final result • can assume that all bound variables are distinct as the reflexive, transitive closure of one-step reduction for any particular term tree, do α -renaming to make this so • • “ E → ∗ V ” means “ E reduces to result value V ” • (formalized later) That’s it! Craig Chambers 170 CSE 505 Craig Chambers 171 CSE 505
Examples Substitution Substitution is suprisingly tricky • must avoid changing the meaning of any variable reference, in either substitutee or substituted expressions • “capture-avoiding substitution” Define formally by cases, over the syntax of the substitutee: • identifiers: [ E 2 / I ] I = E 2 [ E 2 / I ] J = J (if J ≠ I ) • applications: [ E 2 / I ]( E 1 E 3 ) = ([ E 2 / I ] E 1 ) ([ E 2 / I ] E 3 ) • abstractions: [ E 2 / I ]( λ I . E ) = λ I . E [ E 2 / I ]( λ J . E ) = λ J .[ E 2 / I ] E (if J ≠ I and J ∉ FV ( E 2 )) • use α -renaming on ( λ J . E ) to ensure J ∉ FV ( E 2 ) Defines the scoping rules of the λ -calculus Craig Chambers 172 CSE 505 Craig Chambers 173 CSE 505 Normal forms Reduction order E → ∗ V : E evaluates fully to a value V Can have several places in an expression where a lambda is applied to an argument • → ∗ defined as the reflexive, transitive closure of → • each is called a redex What is V ? ( λ x.( λ y.x) x) (( λ z.z) ( λ w.( λ v.v) w)) an expression with no opportunities for β -reduction • such expressions are called normal forms Therefore, have a choice in what reduction to make next Can define formally: V ::= λ I . V Which one is the right one to choose to reduce next? | I V | I Does it matter? (I.e., any E except one containing ( λ I . E 1 ) E 2 somewhere) • to the final result? • to how long it takes to compute it? • to whether the result is computed at all? Q: does every λ -calculus term have a normal form? Q: is a term’s normal form unique? Craig Chambers 174 CSE 505 Craig Chambers 175 CSE 505
Some possible reduction strategies Amazing fact #1: Church-Rosser Thm., Part 1 Thm ( Confluence ). If e 1 → ∗ e 2 and e 1 → ∗ e 3 , Example: ( λ x.( λ y.x) x) (( λ z.z) ( λ w.( λ v.v) w)) then ∃ e 4 s.t. e 2 → ∗ e 4 and e 3 → ∗ e 4 . normal-order reduction: e 1 always choose leftmost, outermost redex e 2 e 3 • call-by-name, lazy evaluation: same, and ignore redexes underneath λ e 4 applicative-order reduction: always choose leftmost, outermost redex whose argument is in normal form Corollary ( Normalization ). Every term has a unique normal • call-by-value, eager evaluation: form, if it exists same, and ignore redexes underneath λ • No matter what reduction order is used! Proof? [e.g. by contradiction] Again, does it matter? • to the final result? • to how long it takes to compute it? • to whether the result is computed at all? Craig Chambers 176 CSE 505 Craig Chambers 177 CSE 505 Existence of normal form? Amazing fact #2: Church-Rosser Thm., Part 2 Does every term have a normal form? Thm. If a term has a normal form, then normal-order reduction will find it! • (If it does, we already know it’s unique) • applicative-order reduction might not! Consider: ( λ x.x x) ( λ x.x x) Example: ( λ x.( λ y.y)) (( λ z.z z) ( λ z.z z)) Same example, but using abbreviations: id ≡ ( λ y.y) loop ≡ (( λ z.z z) ( λ z.z z)) ( λ x. id ) loop (Abbreviations are not really in the λ -calculus; expand away textually before evaluating) Q: How can I tell whether a term has a normal form? Craig Chambers 178 CSE 505 Craig Chambers 179 CSE 505
Recommend
More recommend