- By
**matty** - Follow User

- 360 Views
- Uploaded on

Download Presentation
## the lambda calculus

**An Image/Link below is provided (as is) to download presentation**

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript

the lambda calculus

- Originally, the lambda calculus was developed as a logic by Alonzo Church in 1932
- Church says: “There may, indeed, be other applications of the system than its use as a logic.”
- Dave says: “I’ll say”

Reading

- Pierce, Chapter 5

functions

- essentially every full-scale programming language has some notion of function
- the (pure) lambda calculus is a language composed entirely of functions
- we use the lambda calculus to study the essence of computation
- it is just as fundamental as Turing Machines

syntax

- the identity function:
- \x.x
- 2 notational conventions:
- applications associate to the left (like in ML):
- “y z x” is “(y z) x”
- the body of a lambda extends as far as possible to the right:
- “\x.x \z.x z x” is “\x.(x \z.(x z x))”

terminology

- \x.t
- \x.x y

the scope of x is the term t

y is free in the term \x.x y

x is bound

in the term \x.x y

CBV operational semantics

- single-step, call-by-value OS: t --> t’
- values are v ::= \x.t
- primary rule (beta reduction):
- t [v/x] is the term in which all free occurrences of x in t are replaced with v
- this replacement operation is called substitution
- we will define it carefully later in the lecture

(\x.t) v --> t [v/x]

operational semantics

- search rules:
- notice, evaluation is left to right

e1 --> e1’

e1 e2 --> e1’ e2

e2 --> e2’

v e2 --> v e2’

Example

(\x. x x) (\y. y)

Another example

(\x. x x) (\x. x x)

Another example

(\x. x x) (\x. x x)

--> x x [\x. x x/x]

== (\x. x x) (\x. x x)

- In other words, it is simple to write non terminating computations in the lambda calculus
- what else can we do?

We can do everything

- The lambda calculus can be used as an “assembly language”
- We can show how to compile useful, high-level operations and language features into the lambda calculus
- Result = adding high-level operations is convenient for programmers, but not a computational necessity
- Result = make your compiler intermediate language simpler

Let Expressions

- It is useful to bind intermediate results of computations to variables:

let x = e1 in e2

- Question: can we implement this idea in the lambda calculus?

source = lambda calculus + let

translate/compile

target = lambda calculus

Let Expressions

- It is useful to bind intermediate results of computations to variables:

let x = e1 in e2

- Question: can we implement this idea in the lambda calculus?

translate (let x = e1 in e2) =

(\x.e2) e1

Let Expressions

- It is useful to bind intermediate results of computations to variables:

let x = e1 in e2

- Question: can we implement this idea in the lambda calculus?

translate (let x = e1 in e2) =

(\x. translate e2) (translate e1)

Let Expressions

- It is useful to bind intermediate results of computations to variables:

let x = e1 in e2

- Question: can we implement this idea in the lambda calculus?

translate (let x = e1 in e2) =

(\x. translate e2) (translate e1)

translate (x) = x

translate (\x.e) = \x.translate e

translate (e1 e2) = (translate e1) (translate e2)

booleans

- we can encode booleans
- we will represent “true” and “false” as functions named “tru” and “fls”
- how do we define these functions?
- think about how “true” and “false” can be used
- they can be used by a testing function:
- “test b then else” returns “then” if b is true and returns “else” if b is false
- the only thing the implementation of test is going to be able to do with b is to apply it
- the functions “tru” and “fls” must distinguish themselves when they are applied

booleans

tru = \t.\f. t fls = \t.\f. f

test = \x.\then.\else. x then else

eg:

test tru (\x.t1) (\x.t2)

-->* (\t.\f. t) (\x.t1) (\x.t2)

-->* \x.t1

booleans

- what is wrong with the following translation?

translate true = tru

translate false = fls

translate (if e1 then e2 else e3)

= test (translate e1) (translate e2) (translate e3)

...

booleans

- what is wrong with the following translation?

translate true = tru

translate false = fls

translate (if e1 then e2 else e3)

= test (translate e1) (translate e2) (translate e3)

...

-- e2 and e3 will both be evaluated regardless

of whether e1 is true or false

-- the target program might not terminate

in some cases when the source program would

pairs

- would like to encode the operations
- create e1 e2
- fst p
- sec p
- pairs will be functions
- when the function is used in the fst or sec operation it should reveal its first or second component respectively

pairs

create = \fst.\sec.\bool. bool fst sec

fst = \p. p tru

sec = \p. p fls

fst (create tru fls)

-->* fst (\bool. bool tru fls)

-->* (\bool. bool tru fls) tru

-->* tru

and we can go on...

- numbers
- arithmetic expressions (+, -, *,...)
- lists, trees and datatypes
- exceptions, loops, ...
- ...
- the general trick:
- values will be functions – construct these functions so that they return the appropriate information when called by an operation

Formal details

- In order to be precise about the operational semantics of the lambda calculus, we need to define substitution properly
- remember the primary evaluation rule:

(\x.t) v --> t [v/x]

substitution: a first try

- the definition is given inductively:

x [t/x] = t

y [t/x] = y (if y ≠ x)

(\y.t’) [t/x] = \y.t’ [t/x]

t1 t2 [t/x] = (t1 [t/x]) (t2 [t/x])

substitution: a first try

- This works well 50% of the time
- Fails miserably the rest of the time:

(\x. x) [y/x] = \x. y

- the x in the body of (\x. x) refers to the argument of the function
- a substitution should not replace the x
- we got “unlucky” with our choice of variable names

substitution: a first try

- This works well 50% of the time
- Fails miserably the rest of the time:

(\x. z) [x/z] = \x. x

- the z in the body of (\x. z) does not refer to the argument of the function
- after substitution, it does refer to the argument
- we got “unlucky” with our choice of variable names again!

calculating free variables

- To define substitution properly, we must be able to calculate the free variables precisely:

FV(x) = {x}

FV(\x.t) = FV(t) / {x}

FV(t1 t2) = FV(t1) U FV(t2)

substitution

x [t/x] = t y [t/x] = y (if y ≠ x)

(\y.t’) [t/x] = \y.t’ (if y = x)

(\y.t’) [t/x] = \y.t’ [t/x] (if y ≠ x and y FV(t))

t1 t2 [t/x] = (t1 [t/x]) (t2 [t/x])

substitution

- almost! But the definition is not exhaustive
- what if y ≠ x and y FV(t) in the case for functions:

(\y.t’) [t/x] = \y.t’ (if y = x)

(\y.t’) [t/x] = \y.t’ [t/x] (if y ≠ x and y FV(t))

alpha conversion

- the names of bound variables are unimportant (as far as the meaning of the computation goes)
- in ML, there is no difference between
- fn x => x and fn y => y
- we will treat \x. x and \y. y as if they are (absolutely and totally) indistinguishable so we can always use one in place of the other

alpha conversion

- in general, we will adopt the convention that terms that differ only in the names of bound variables are interchangeable
- ie: \x.t == \y. t[y/x] (where this is the latest version of substitution)
- changing the name of a bound variable is called alpha conversion

substitution, finally

x [t/x] = t y [t/x] = y (if y ≠ x)

(\y.t’) [t/x] = \y.t’ [t/x] (if y ≠ x and y FV(t))

t1 t2 [t/x] = (t1 [t/x]) (t2 [t/x])

we use alpha-equivalent terms so

this constraint can always be

satisfied. We pick y as we like

so this is true.

operational semantics again

- Is this the only possible operational semantics?

(\x.t) v --> t [v/x]

e1 --> e1’

e1 e2 --> e1’ e2

e2 --> e2’

v e2 --> v e2’

alternatives

(\x.t) e --> t [e/x]

(\x.t) v --> t [v/x]

e1 --> e1’

e1 e2 --> e1’ e2

e1 --> e1’

e1 e2 --> e1’ e2

e2 --> e2’

v e2 --> v e2’

call-by-value

call-by-name

alternatives

(\x.t) e --> t [e/x]

(\x.t) v --> t [v/x]

e1 --> e1’

e1 e2 --> e1’ e2

e1 --> e1’

e1 e2 --> e1’ e2

e2 --> e2’

e1 e2 --> e1 e2’

e2 --> e2’

v e2 --> v e2’

e --> e’

\x.e --> \x.e’

call-by-value

full beta-reduction

alternatives

(\x.t) e --> t [e/x]

(\x.t) v --> t [v/x]

e1 --> e1’ e1 ≠ v

e1 e2 --> e1’ e2

e1 --> e1’

e1 e2 --> e1’ e2

e2 --> e2’

v e2 --> v e2’

e2 --> e2’

v e2 --> v e2’

e --> e’

\x.e --> \x.e’

call-by-value

normal-order reduction

alternatives

(\x.t) v --> t [v/x]

(\x.t) v --> t [v/x]

e1 --> e1’

e1 e2 --> e1’ e2

e1 --> e1’

e1 v --> e1’ v

e2 --> e2’

v e2 --> v e2’

e2 --> e2’

e1 e2 --> e1 e2’

call-by-value

right-to-left call-by-value

summary

- the lambda calculus is a language of functions
- Turing complete
- easy to encode many high-level language features
- the operational semantics
- primary rule: beta-reduction
- depends upon careful definition of substitution
- many evaluation strategies

Download Presentation

Connecting to Server..