# The Lambda Calculus

Or: Wikipedia and Nightmares

## Review

Functions

Recursion

First Class Functions

Closure

Currying

## Functions

Core concept

"Take input and return output"

abstract some processing

``````def add(x: int, y: int) -> int:
return x + y

int add(int x, int y) {
return x + y;
}

def my_print(string: str):
print(string)``````

## Recursion

We've used it

when a function calls itself

``````def factorial(x):
if x == 1:
return 1
else:
return x * factorial(x-1) ``````

## "First Class"

Might be new

functions are data and can be manipulated as such

essentially:

a function is a type that you can manipulate in the same way you can manipulate int or String

``````def map(f: Function[T] -> T, li: List[T]) -> List[T]:
result = []
for item in li:
result.append(f(li))
return result

def double(x: int) -> int:
return x * 2

map(double, [1,2,3])  # [2,4,6]``````

## Closure

Defining a Function within a function

variables in outer scopes can be accessed from inner ones

when you call an outer function it returns a new inner function bound to a variable

``````def map(f, li):
result = []
for item in li:
result.append(f(li))
return result

return x + val

## Currying

Functions need only one arg

without loss of generality:

this can be applied to functions of any number of arguments

what is the type signature of add_curried?

``````def add(x, y):
return x + y

return x + y

# of type int``````

### Type Signature

Add curried take in an integer

it returns a function

that function takes in an int and returns an int

``````def add_curried(x: int) -> Function[int] -> int:
return x + y
``````

## Simplification

We strip things away

Functions have no side effects

Functions always return something

Functions are immediately used

(reductions!)

No need for a return statement

No need for a name

## Lambdas

All functions can be either immediately invoked

``````add = lambda x, y: x + y  # eww!

(lambda x, y: x + y)(2,3) == 5

# Putting it together with previous slides

(lambda x: lambda y : x + y)(2)(3) == 5

(lambda x:
lambda y:  # returned by the function of x
x + y  # returned by the function of y
)  # <- this is f(x) that returns f(y)
(2)  # <- this is f(y) that returns an int
(3)  # <- this is an integer``````

## Lambdas

Or immediately passed into another function

``````(lambda f, x, y: f(x) + f(y))
(lambda n: n + 1, 2, 3) == 7
#  or
(lambda f:
lambda x:
lambda y:
f(x) + f(y)
)(lambda n: n + 1)(2)(3)``````

### Lambda Calculus

Because mathematicians hate you

replace "lambda" with "λ"

replace "x:" with "x."

Now for some examples

## Lambda Calculus

``````x
"a value x"

t(x)
f(t)(x)  # vaguely equivalent to f(t, x)
"the function t called with the value x"

lambda x: t
"the function that takes in x and returns t"``````
``````x  # is valid
a "lambda term"

tx  # is also valid
ftx  # is equivalent to ((ft)x)
an "application"

λx.t  # is valid
an "abstraction"``````

That's it

Those are the rules

## Some Basic Functions

``````lambda x: x

lambda f: lambda x: f(x)``````
``````λx.x
the identity function

λf.λx.fx
the function "apply"``````

## Church Numerals

``````lambda f: lambda x: x
lambda f: lambda x: f(x)
lambda f: lambda x: f(f(x))``````
``````λf.λx.x  # Zero
λf.λx.fx  # One, this is the same as "apply"
λf.λx.f(fx)  # Two

``````

## Church Numerals

``````λf.λx.x  # Zero
λf.λx.fx  # One, this is the same as "apply"
λf.λx.f(fx)  # Two

``````

huh?

## Church Numerals

``````λf.λx.x  # Zero
λf.λx.fx  # One, this is the same as "apply"
λf.λx.f(fx)  # Two

``````

Type signature of this function?

``````(Function[T] -> T) -> Function[T] -> T

we can simplify this to

(T -> T) -> T -> T``````

## Resolution

``````λf.λx.x 1+ 0
λf=(1+).λx.x 0
λx.x 0
λx=0.x 0
λx=0.0
0``````
``````λf.λx.fx 1+ 0
λf=(1+).λx.fx 0
λf=(1+).λx.1+x 0
λx.1+x 0
λx=0.1+x
λx=0.1+0
1+0``````
``````λf.λx.f(fx) 1+ 0
λf=(1+).λx.f(fx) 0
λf=(1+).λx.1+1+x 0
λx.1+1+x 0
λx=0.1+1+x
λx=0.1+1+0
1+1+0``````
``````f = 1+
x = 0``````

## In Code

``````(lambda f:
lambda x:
f(f(x))  #two
)(lambda n: n + 1)(0) == 2``````
``````(lambda f:
lambda x:
return f(f(x))  #two
)(lambda n: {n} | {i for i in n})({})
== {{}, {{}}}  # two in ZF axioms``````

## More useful functions

``````λf.λz.fz  # one

λn.λf.λz.f (n f z)  # "successor"``````
``````(T -> T) -> T -> T

? -> (T -> T) -> T -> T``````
``````(T -> T) -> T -> T

? -> (T -> T) -> T -> T

(nfz) means that n is a function on two args,
f and z
f is of type (T -> T) and z is of type T

so n is type (T -> T) -> T -> T``````
``````(T -> T) -> T -> T

? -> (T -> T) -> T -> T

(nfz) means that n is a function on two args,
f and z
f is of type (T -> T) and z is of type T

so n is type (T -> T) -> T -> T

we know some things with that type signature:
Church Numerals``````
``````(T -> T) -> T -> T

C -> (T -> T) -> T -> T

(nfz) means that n is a function on two args,
f and z
f is of type (T -> T) and z is of type T

so n is type (T -> T) -> T -> T

we know some things with that type signature:
Church Numerals``````

## More useful functions

``````λf.λz.fz  # one

λn.λf.λz.f (n f z)  # "successor"

λm.λn.λf.λz. m f (n f z)  # "plus"``````
``````(T -> T) -> T -> T

C -> (T -> T) -> T -> T

? -> C -> (T -> T) -> T -> T``````
``````(T -> T) -> T -> T

C -> (T -> T) -> T -> T

? -> C -> (T -> T) -> T -> T

(nfx) resolves to type T
f is type (T -> T)
so m is of type (T -> T) -> T -> T``````
``````(T -> T) -> T -> T

C -> (T -> T) -> T -> T

C -> C -> (T -> T) -> T -> T

(nfx) resolves to type T
f is type (T -> T)
so m is of type (T -> T) -> T -> T``````

## Another Example

``````λf.λz.fz  # one

λn.λf.λz.f (n f z)  # "successor"

λm.λn.λf.λz. m f (n f z)  # "plus"``````
``````m = ONE = λg.λx.gx
n = ONE = λh.λy.hy
f = 1+                     arg 1   2    3  4
z = 0                        \/   \/   \/ \/
(λm.λn.λf.λz. m f (n f z)) (ONE) (ONE) 1+ 0
λm=(ONE).λn.λf.λz. m f (n f z) (ONE) 1+ 0
λm=(ONE).λn.λf.λz.λg.λx.g x f (n f z) (ONE) 1+ 0
λn.λf.λz.λg.λx.g x f (n f z) (ONE) 1+ 0
λn=(ONE).λf.λz.λg.λx.g x f (n f z) 1+ 0
λn=(ONE).λf.λz.λg.λx.g x f (ONE n f z) 1+ 0
λf.λz.λg.λx.g x f (ONE f z) 1+ 0
λf=(1+).λz.λg.λx.g x f (ONE f z) 0
λf=(1+).λz.λg.λx.g x 1+ (ONE 1+ z) 0
λz.λg.λx.g x 1+ (ONE 1+ z) 0
λz=0.λg.λx.g x 1+ (ONE 1+ z)
λz=0.λg.λx.g x 1+ (ONE 1+ 0)
λg=(1+).λx.g x (ONE 1+ 0)
λg=(1+).λx.1+ x (ONE 1+ 0)
λx.1+ x (ONE 1+ 0)
λx.1+ x (λh.λy.hy 1+ 0)
λx=(λh.λy.hy 1+ 0).1+ x
1+ λh.λy.hy 1+ 0
1+ λh=(1+).λy.hy 1+ 0
1+ λh=(1+).λy.1+y 0
1+ λy.1+y 0
1+ λy=0.1+y
1+ λy=0.1+0
1+1+0  # == 2!``````

## Turing Machines

### Turing Complete

To prove turing completeness, we need to show that we can simulate a turing machine

So we'll prove that we can simulate an arbitrary finite state machine as well as "memory"

A state is just a type, they could be integers

A transition is simply a function from a previous state to some future state

### Turing Complete

That leaves: A tape/memory

### Turing Complete

These *are* conditionals

``````λx.λy.x  # True
λx.λy.y  # False``````

### And Tuples

A Tuple holds 2 things

``````λx.λy.λf.f x y

x is the first thing
y is the second thing
f is the "getter function"

so
(λx.λy.λf.f x y) "A" "B" is a pair
λf.f "A" "B"``````

### Tuple Pickers

"The first thing in

the tuple of

"A" and "B"

``````(λp.p λx.λy.x)  # First
(λp.p λx.λy.y)  # Second

λx.λy.x  # is True
λx.λy.y  # is False

(λp.p λx.λy.x) (λx.λy.λf.f x y) "A" "B"
(λp.p λx.λy.x) (λf.f "A" "B")
(λf.f "A" "B") λx.λy.x
λx.λy.x "A" "B"
"A"``````

A tuple whose first item is

a value and second item is another tuple

``````("A", | )
-> ("B", | )
-> ("C" | )
-> ("D" \ )``````

### Turing Complete!

That's it!

we have the pieces we need

- A Tape (memory)

- Transition functions with conditionals

- States

### Putting it all Together

How do we get to the next item in a linked list?

``````λx.λy.λf.f x y  # a linked list node (tuple)
(λp.p) λx.λy.y  # "SECOND"``````
``````λx.λy.λf.f x y  # a linked list node (tuple)
(λp.p) λx.λy.y  # "SECOND"

(λn.λf.λx.f (n f x)) n SECOND HEAD``````
``````λx.λy.λf.f x y  # a linked list node (tuple)
(λp.p) λx.λy.y  # "SECOND"

(λn.λf.λx.f (n f x)) n SECOND HEAD

``````("A", | )
-> ("B", | )
-> ("C" | )
-> ("D" \ )``````

## Useful?

Can be fast

Can parallelize everything (fast)

Efficient way of thinking

``````λx.x == λy.y  # α-conversion
x=("Hello") == "Hello"  # substitution
λx=("Hello").x == "Hello"  # β-reduction
λx.(f x) == f  # η-conversion``````

## Combinators

Y?

``λh.((λx.h (x x)) (λx.h (x x))) g``
``````λh.((λx.h (x x)) (λx.h (x x))) g
λh=g.((λx.h (x x)) (λx.h (x x)))
λx.g (x x) (λx.g (x x))
λx=(λx.g (x x)).g (x x)
g (λx.g (x x) λx.g (x x))``````
``````λh.((λx.h (x x)) (λx.h (x x))) g
λh=g.((λx.h (x x)) (λx.h (x x)))
λx.g (x x) (λx.g (x x))
λx=(λx.g (x x)).g (x x)``````
``````λh.((λx.h (x x)) (λx.h (x x))) g
λh=g.((λx.h (x x)) (λx.h (x x)))``````
``````λh.((λx.h (x x)) (λx.h (x x))) g
λh=g.((λx.h (x x)) (λx.h (x x)))
λx.g (x x) (λx.g (x x))``````
``````λh.((λx.h (x x)) (λx.h (x x))) g
λh=g.((λx.h (x x)) (λx.h (x x)))
λx.g (x x) (λx.g (x x)) -----
λx=(λx.g (x x)).g (x x)     |
g (λx.g (x x) λx.g (x x))   |
λx.g (x x) λx.g (x x) <--|``````

## Combinators

Y?

This is the Y combinator

recursion!

``````λh.((λx.h (x x)) (λx.h (x x))) g
λh=g.((λx.h (x x)) (λx.h (x x)))
λx.g (x x) (λx.g (x x)) -----
λx=(λx.g (x x)).g (x x)     |
g (λx.g (x x) λx.g (x x))   |
λx.g (x x) λx.g (x x) <--|

h(g) == g(h(g))
``````

By Joshua Morton

• 621