This week I learned about the wild wacky world of lambda calculus, and I want to share it with everyone because it’s cool and also kinda stupid, so it should be fun.
If you look up what lambda calculus is, Wikipedia will tell you “Lambda calculus is a formal system in mathematical logic for expressing computation based on function abstraction and application using variable binding and substitution.” Basically, lambda calculus is a really dumb way of saying that it’s a method of computing things using very simple functions. The syntax of performing a function in lambda calculus is literally as simple as it can possibly be because the point of it is to represent complex problems with the most basic functional building blocks there are.
There are only three rules that define what a “lambda term” is, and they go as follows:
Any variable x is a lambda term
If t is a lambda term, so is λx.t (this is called abstraction)
If t1, t2 are lambda terms, so is t1 t2 (this is called application)
These three sentences obviously make little to no sense at all, and I don’t know why they’re the first thing that everyone says when they’re introducing this subject. I prefer to think of the syntax of lambda calculus in more concrete terms. Here’s how I think about it:
The expression λx.t is a function that takes in a variable x as an input and returns a different variable t as an output
The expression t1 t2 means that you are applying a function t1 to the variable t2
Knowing this, we can start to see how functions will be evaluated. For example, the identity function, which takes in an input and then returns that same input, would be written as λx.x, because this function takes in a variable x and then returns that variable x as its output. What comes after the lambda symbol but before the period is an input, and what comes after the period is the output. To put a value into a function we write (λx.x) y which is the same as having written t1 = λx.x and t2 = y, evaluate t1 t2. The applied function (λx.x) y will return the value y because it takes the value y into the function’s input and then the function returns the input it was given as an output.
To look at a more complicated example, we can write a function with two inputs, (λx y.x y). This function will take in two inputs, x and y, and then return the function x applied to the value y. If we wanted to give some sample inputs to this function we could say (λx y.x y) (λa.a) b. Function application is left associative, so the expressions to the left are resolved before the ones to the right. That means, instead of putting b into the identity function (λa.a), we put the function (λa.a) and value b into the function (λx y.x y) as the inputs x and y respectively. This function application can then be simplified like this:
(λx y.x y) (λa.a) b
(λa.a) b
b
I think that next week I’ll get more into why this is actually cool and not just weird and confusing. We can build up all of modern computation from these simple functions, but for now, I’ll leave you with just the syntax and an idea of how to solve the equations you’re given.