CMSC 336: Type Systems for Programming Languages Lecture 5: Simply Typed Lambda Calculus Acar & Ahmed January 24, 2008

Similar documents
CMSC 336: Type Systems for Programming Languages Lecture 4: Programming in the Lambda Calculus Acar & Ahmed 22 January 2008.

Programming Languages

Lecture 9: Typed Lambda Calculus

CIS 500 Software Foundations Midterm I

Concepts of programming languages

Fall 2013 Midterm Exam 10/22/13. This is a closed-book, closed-notes exam. Problem Points Score. Various definitions are provided in the exam.

1 Introduction. 3 Syntax

Lambda Calculus. Type Systems, Lectures 3. Jevgeni Kabanov Tartu,

CIS 500 Software Foundations Fall October 2

Programming Languages Fall 2014

CS 6110 S11 Lecture 25 Typed λ-calculus 6 April 2011

Costly software bugs that could have been averted with type checking

Programming Languages Lecture 14: Sum, Product, Recursive Types

CS-XXX: Graduate Programming Languages. Lecture 9 Simply Typed Lambda Calculus. Dan Grossman 2012

Typed Lambda Calculus and Exception Handling

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011

Mutable References. Chapter 1

Chapter 5: The Untyped Lambda Calculus

Review. CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Let bindings (CBV) Adding Stuff. Booleans and Conditionals

CIS 500 Software Foundations Fall September 25

Lesson 4 Typed Arithmetic Typed Lambda Calculus

Type Systems Winter Semester 2006

Lambda Calculus and Type Inference

Lecture 13: Subtyping

Lambda Calculus: Implementation Techniques and a Proof. COS 441 Slides 15

Pure (Untyped) λ-calculus. Andrey Kruglyak, 2010

The Lambda Calculus. 27 September. Fall Software Foundations CIS 500. The lambda-calculus. Announcements

CS4215 Programming Language Implementation. Martin Henz

上海交通大学试卷 ( A 卷 ) ( 2015 至 2016 学年第 2 学期 )

Lambda Calculus. Lecture 4 CS /26/10

Tradeoffs. CSE 505: Programming Languages. Lecture 15 Subtyping. Where shall we add useful completeness? Where shall we add completeness?

1 Scope, Bound and Free Occurrences, Closed Terms

上海交通大学试卷 ( A 卷 ) ( 2015 至 2016 学年第 2 学期 )

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

CSE-321 Programming Languages 2012 Midterm

Programming Language Features. CMSC 330: Organization of Programming Languages. Turing Completeness. Turing Machine.

CMSC 330: Organization of Programming Languages

Lecture Notes on Data Representation

CMSC 330: Organization of Programming Languages. Lambda Calculus Encodings

CMSC 330: Organization of Programming Languages

CMSC 330: Organization of Programming Languages

CMSC 330: Organization of Programming Languages

More Untyped Lambda Calculus & Simply Typed Lambda Calculus

CSE-321 Programming Languages 2010 Final

λ calculus is inconsistent

CSE-321 Programming Languages 2011 Final

The Untyped Lambda Calculus

(Refer Slide Time: 4:00)

Formal Systems and their Applications

CSE 505, Fall 2008, Midterm Examination 29 October Please do not turn the page until everyone is ready.

Lambda Calculus and Extensions as Foundation of Functional Programming

CMSC 330: Organization of Programming Languages. Operational Semantics

The University of Nottingham SCHOOL OF COMPUTER SCIENCE A LEVEL 4 MODULE, SPRING SEMESTER MATHEMATICAL FOUNDATIONS OF PROGRAMMING ANSWERS

Lecture 5: The Untyped λ-calculus

CSE505, Fall 2012, Midterm Examination October 30, 2012

λ-calculus Lecture 1 Venanzio Capretta MGS Nottingham

COMP 1130 Lambda Calculus. based on slides by Jeff Foster, U Maryland

Goal. CS152: Programming Languages. Lecture 15 Parametric Polymorphism. What the Library Likes. What The Client Likes. Start simpler.

Part III. Chapter 15: Subtyping

CSE 505: Concepts of Programming Languages

We defined congruence rules that determine the order of evaluation, using the following evaluation

COMP 4161 NICTA Advanced Course. Advanced Topics in Software Verification. Toby Murray, June Andronick, Gerwin Klein

The Typed λ Calculus and Type Inferencing in ML

Subsumption. Principle of safe substitution

Types for References, Exceptions and Continuations. Review of Subtyping. Γ e:τ τ <:σ Γ e:σ. Annoucements. How s the midterm going?

Lambda Calculus. Concepts in Programming Languages Recitation 6:

Harvard School of Engineering and Applied Sciences Computer Science 152

The Polymorphic Blame Calculus and Parametricity

Part III Chapter 15: Subtyping

INF 212 ANALYSIS OF PROG. LANGS LAMBDA CALCULUS. Instructors: Crista Lopes Copyright Instructors.

HOL DEFINING HIGHER ORDER LOGIC LAST TIME ON HOL CONTENT. Slide 3. Slide 1. Slide 4. Slide 2 WHAT IS HIGHER ORDER LOGIC? 2 LAST TIME ON HOL 1

Formal Semantics. Aspects to formalize. Lambda calculus. Approach

CSE-321 Programming Languages 2010 Midterm

From the λ-calculus to Functional Programming Drew McDermott Posted

Lecture Notes on Program Equivalence

λ calculus Function application Untyped λ-calculus - Basic Idea Terms, Variables, Syntax β reduction Advanced Formal Methods

CIS 500 Software Foundations Midterm I Answer key October 8, 2003

Shared Variables and Interference

11/6/17. Outline. FP Foundations, Scheme. Imperative Languages. Functional Programming. Mathematical Foundations. Mathematical Foundations

Chapter 5: The Untyped Lambda Calculus

Gradual Typing for Functional Languages. Jeremy Siek and Walid Taha (presented by Lindsey Kuper)

Type Systems. Parametric Polymorphism. 1. Recall Let-Polymorphism. 1. Recall Let-Polymorphism. Lecture 9 Dec. 15th, 2004 Sebastian Maneth

Symmetry in Type Theory

The Substitution Model

The Untyped Lambda Calculus

Shared Variables and Interference

Part VI. Imperative Functional Programming

AXIOMS OF AN IMPERATIVE LANGUAGE PARTIAL CORRECTNESS WEAK AND STRONG CONDITIONS. THE AXIOM FOR nop

Simply-Typed Lambda Calculus

Programming Languages Lecture 15: Recursive Types & Subtyping

Lecture 3: Recursion; Structural Induction

CS 4110 Programming Languages & Logics. Lecture 17 Programming in the λ-calculus

Semantics of programming languages

Programming Language Concepts: Lecture 19

Formal Semantics. Prof. Clarkson Fall Today s music: Down to Earth by Peter Gabriel from the WALL-E soundtrack

Featherweight Java (FJ)

The University of Nottingham

Lambda Calculus and Type Inference

The Substitution Model. Nate Foster Spring 2018

More Lambda Calculus and Intro to Type Systems

Transcription:

CMSC 336: Type Systems for Programming Languages Lecture 5: Simply Typed Lambda Calculus Acar & Ahmed January 24, 2008 Contents 1 Solution to the Exercise 1 1.1 Semantics for lambda calculus....................... 1 1.2 De Bruijn Indices.............................. 3 2 Introduction 4 3 Simply Typed Lambda Calculus 4 4 Types 5 5 Type Safety 9 6 Properties of Typing 9 1 Solution to the Exercise 1.1 Semantics for lambda calculus The problem was to give a call by value and call by name semantics for lambda calculus and show that both are deterministic. Let s try to give a semantics for lambda calculus and see how the different evaluation strategies effect the decisions that we make. As usual, let s start by writing out the syntax for lambda terms. t : : = x λx.t t 1 t 2 First, we need to determine what we consider a value. For both call-byvalue and call-by-name, lambda abstractions are values because no reductions are allowed inside them. Note that in full beta-reduction and in the normalorder strategy, we do not consider lambda abstractions as values, e.g., the term λ x. (λ x. x) x is not a value, because there is a redex inside the abstraction. We now consider each term and give evaluation rules for them. We don t need an evaluation rule for variables, because we don t want to see a free (unbound) variable during evaluation that would be an error. Consider lambda abstractions. They are values. Since we are giving a smallstep semantics, we need no rules for values. Consider application, t 1 t 2. We know that we will apply beta reduction when t 1 is a lambda abstraction, i.e. t 1 = λx.t. So we know what to do when 1

t 1 is a value, we will simply apply beta reduction. If t 1 is not a value, then we will simply evaluate it recursively. How about t 2? Regardless of whether t 2 is a value or not, we can always use it in a beta reduction. So we have a choice to make here: we can apply beta reduction immediately or evaluate t 2 to a value first. This is where we consult the evaluation strategy. In call-by-name, we apply beta reduction immediately. In call by value we evaluate it. Here is a possible call-by-value semantics. t 1 t 1 t 1 t 2 t 1 t 2 t 2 t 2 t 1 t 2 t 1 t 2 (λx.t)v [v/x] t Here is a possible call-by-name semantics. t 1 t 1 t 1 t 2 t 1 t 2 (λx.t) t 2 [t 2 /x] t Is there anything strange with these? Note that the call-by-value semantics is non-deterministic, because when we have t 1 t 2, two rules apply. Since call-by-value semantics requires that both parts of an application be values before applying beta reduction, we need to figure which part we will reduce first. There is another choice to make here to determine the evaluation order. Typically, we reduce the left part of the application first. With this evaluation order, call-by-value corresponds to leftmostoutermost evaluation. Here is one of the two possible deterministic call-by-value semantics. t 1 t 1 t 1 t 2 t 1 t 2 t 2 t 2 (λx.t 1 ) t 2 (λx.t 1 ) t 2 (λx.t)v [v/x] t We can now show that semantics is deterministic. We need to prove that if t t and t t then t = t. I will do the proof for call by value (the case for call by name is symmetric). The proof is by induction on the depth of 2

the derivation trees of the two reductions. When the depth of the derivation is 1, we know that we have an application. Since there is only one rule that we can possibly apply, the property holds. Suppose now that the property holds for all derivation trees of depth d or less. Consider now a derivation tree with depth d + 1 > 1. Consider now the last evaluation rules, if t = t 1 t 2 and t 1 is a lambda abstraction, then by induction we know that t 2 will be evaluated deterministically and the lemma holds. If t 1 is not a lambda term, then there is only one rule to apply and the property holds by induction. 1.2 De Bruijn Indices The de Bruijn notation of tru := λx.λy.x is λ.λ.1. The de Bruijn notation of fls := λx.λy.y is λ.λ.0. The de Bruijn notation for pair := λx 1.λx 2.λy. y x 1 x 2 is λ.λ.λ.0 2 1. For applying beta reduction in the de Bruijn notation, you needed to use the formula for substitution given in your notes. To define substitution on de Bruijn terms, we first need to define a shift operation that increases the indices of all free variables by a specified number. To know what variables are free, however, we need to know how deep we are in a term (in the abstract syntax tree). This is specified by a cutoff constant c. In the formula below, d is specifies by how much we want to shift the free variables. d c (i) = i if i < c d c (i) = i + d if i c d c (λ.t) = λ. d c+1 (t) We will write i (t) to mean i 0 (t). Examples: 1. 2 (λ.λ.1(0 2)) = 2. 2 (λ.01(λ.0 1 2)) = d c (t 1 t 2 ) = ( d c (t 1 )) ( d c (t 2 )) We define substitution as follows. Shifting during substation is necessary to make sure that free variables of the substituted term still point to the same variables. [t/j]i = t if i = j [t/j]i = i if i j [t/j]λ.t = λ.[ 1 (t)/j + 1]t [t/j]t 1 t 2 = [t/j]t 1 [t/j]t 2 3

Figure 1: λ.(λ.λ.10) (λ.1) β λ.(λ.λ.2 0) illustrated We define beta reduction by substituting the term for the 0 th variables and shifting the term by 1, because beta reduction strips one lambda off the term. 2 Introduction (λ.t 1 )t 2 = 1 ([ 1 (t 2 )/0]t 1 ) So far, we have considered a few languages (e.g., a language for numbers, untyped lambda calculus) and gave a operational semantics for them. We observed several times that the operational semantics is not able to evaluate any term. In practice, this means that we would get stuck if we try to evaluate a term that the operational semantics does not know how to handle. An example of such a term is if (λ x.x) then true else false. Unsafe languages such as C/C++ can refuse to continue execution when they encounter such terms (e.g., core dump ). In this lecture, we will start learning some neat ideas for designing languages that don t get stuck or go wrong. 3 Simply Typed Lambda Calculus Values v : : = true false λx.t Terms t : : = v if t then t else t t t Let s give a call-by-value operational semantics for this 4

t 1 t 1 if t 1 then t 2 else t 3 if t 1 then t 2 else t 3 if true then t 2 else t 3 t 2 if false then t 2 else t 3 t 3 t 1 t 1 t 1 t 2 t 1 t 2 t 2 t 2 t 1 t 2 t 1 t 2 (λx.t) v [v/x] t Note now that this semantics gets stuck when we try to evaluate the term if λx.x then true else false. 4 Types Think of all the terms that can be generated with the abstract syntax. The problem is that some of these terms cannot be evaluated using our operational semantics. So we will try to identify the terms that the operational semantics can evaluate. We will put some structure to the set of terms by dividing it into into disjoint subsets. Informally, we will place the terms that the operational semantics treats in the same way into the same subset. We will then give a name to each subsets which will be its type. The goal is to be able to say that if a term is well typed, then operational semantics will be able to evaluate the term to a value (barring infinite evaluation). We have been vague about how we decide that the operational semantics treats two terms similarly (so that we can give them the same type). More precisely, we will assign two terms the same type if the operational semantics can evaluate them to a value of that type. So we can build our type system by starting with the values, giving them a type and figuring out the types of other more complex terms. Since we have booleans in our language, we will have a type of bool type. For a term t, we will write t : τ to say that t has type τ. So we have true:bool and false:bool. Let s now start figuring out the type for other terms. We will do this inductively by using inference rules. 5

Before we go on, we will need to figure out a type for functions, but should their type be? The type of a function should include its return type, because when we apply a function that is what it will evaluate to. How about the argument type? It is important to include that as well so that we can make sure that when a function is applied to an argument, the argument has the correct type. Now here is a first try to a set of type inference judgements: true : bool false : bool t 1 : bool t 2 : τ t 3 : τ if t 1 then t 2 else t 3 : τ t : τ 2 λx.t : τ 1 τ 2 t 1 : τ 1 τ 2 τ 2 : τ 1 t 1 t 2 : τ 2 Note that for the if statement we insist that both branches have the same type. In other words, we insist that both branches return values of the same type when evaluated. This is a conservative assumption, because we only need to know that the branch that ends up being evaluated returns the desired type. In particular, consider the following statement if true then true else (λ x.x) always returns true. But it is not a well-typed term. The reason for this assumption is that we want to be able to determine the type of the term by looking at it we don t want to execute it. Execution would be too late. The idea here is to prevent execution time errors by designing a safe language. In other words, we want to be able to check a term and determine that it is safe to execute by simply inspecting it. Question: This type system is broken. Why? The problem is that it accepts very few terms. Consider for example the function λx.if x then true else false. We can assign a type to this term using the given judgments. In particular, if we apply the rules above, we will want to derive the relation if x then true else false : bool but this requires that we know the type of x. But we don t have a way of knowing the type of x. To typecheck terms that have free variables in them, we will enrich our type system with a (typing) context for variables. We define a context for variables, written Γ as a function that maps variables to types. We define Γ as either empty, or a sequence of variable typings as follows: Γ : : = Γ, x : τ 6

We can now update our type system with the context. Γ true : bool Γ false : bool Γ t 1 : bool Γ t 2 : τ Γ t 3 : τ Γ if t 1 then t 2 else t 3 : τ Γ, x : τ 1 t : τ 2 Γ λx.t : τ 1 τ 2 Γ t 1 : τ 1 τ 2 Γ τ 2 : τ 1 Γ t 1 t 2 : τ 2 Note that we simply thread the context Γ through the rules, except that at function definitions. There is one more thing that is not quite satisfactory with this type system. What is the type for λx.x? It can be any function type really, e.g., it can be bool bool, (bool bool) (bool bool). We always want types to be unique, i.e., we never want a term have more than one type. So we will extend our language so that we have type annotations at variable binding locations. Figure 2 shows our updated syntax and the type system. 7

Types τ : : = bool τ 1 τ 2 Values v : : = true false λx : τ.t Terms t : : = v t t if t then t else t Context Γ : : = Γ, x : τ Γ true : bool Γ false : bool x : τ Γ Γ x : τ Γ t 1 : bool Γ t 2 : τ Γ t 3 : τ Γ if t 1 then t 2 else t 3 : τ Γ, x : τ 1 t : τ 2 Γ λx : τ 1.t : τ 1 τ 2 Γ t 1 : τ 1 τ 2 Γ τ 2 : τ 1 Γ t 1 t 2 : τ 2 Figure 2: Simply typed lambda calculus with booleans and its type system. 8

5 Type Safety We now have a set of typing rules for simply typed lambda calculus. We do not know, however, that type system is faithful to the operational semantics. Recall that our motivation is to design a language where well typed terms do not get stuck during evaluation when evaluating the term we never want to be in a situation where we don t know how to continue evaluating that term. We will need to prove type safety. First remember that if we have a well typed term, then we want to be able to evaluate this term. Let s write this down more precisely: If t : τ, then either t is a value, or t t. This property says that if we are given well typed term, then the term is either a value (in this case we are done evaluating this term), or the term reduces to another term t. This property is often called progress. Note that progress property ties together types (the static semantics) and the operational semantics (dynamic semantics). Does this suffice? Not quite. What about a semantics that evaluates a function to the constant true. Instead, we will insist that evaluation preserves typing. More precisely, If Γ t : τ and t t, then Γ t : τ. This is often referred to as the preservation theorem. Progress and preservation gives us some important invariants about about language. They tell us that if we start with a program (a closed term) in our language, then either that term is a value, or the term can be reduced to another term without changing its type. 6 Properties of Typing Before we prove type safety, let s talk about some basic properties of typing and the structures that we use to state the type system. We will not prove these properties but I highly recommend that you do prove them yourself. The proofs should not be hard. These properties primarily give you some vocabulary for talking about type systems. Lemma 1 (Inversion (of the typing relation)) If Γ x : τ, then x : τ Γ. If Γ λx : τ 1.t 2 : τ, then τ = τ 1 τ 2 and Γ, x : τ 1 t 2 : τ 2. If Γ t 1 τ 2 : τ, then there is some τ 1 such that Γ t 1 : τ 1 τ and Γ t 2 : τ 1. If Γ true : τ, then τ = Bool. If Γ false : τ, then τ = Bool. 9

If Γ if t 1 then t 2 else t 3 : τ then Γ t 1 : bool, Γ t 2 : τ and Γ t 3 : τ. This lemma simply inverts the typing judgements. Since we define the type relation t : τ to be the minimum relation that is derivable by application of typing judgements, we can invert the type relation for each term. Exercise: Show that given a context Γ, a term t has at most one type under Γ. (uniqueness of typing). Note that this holds for this type systems. It may not hold for others. We now state what is known as a canonical forms lemma. This lemma states the forms of the values of each type. Lemma 2 (Canonical Forms) 1. If v is a value and v : bool, then v = true or v = false. 2. If v is value and v : τ 1 τ 2, then v = λx : τ 2.t. Lemma 3 (Permutation) If Γ t : τ and is any permutation of Γ, then t : τ. Lemma 4 (Weakening) If Γ t : τ and x dom(γ), then Γ, x : τ t : τ. 10