# Making Sense of Lambda Calculus 3: Truth or Dare With Booleans

By Artyom Bologov

With numbers out of the way, we can finally get to booleans—a simple yet quite atypical topic (at least in its Lambda Calculus rendering). What I'm used to since my university times is truth tables for boolean ops. Lambda Calculus (LC) booleans work in an absolutely different way. Abandon tables ye who enters here.

True and False, the fundamental booleans, are simple: Just take two things and return the first (true) or second (false).

Coincidentally, `false`

is the same function as 0 and null list (ooops, spoilers!)
Convenient for me as a C programmer used to `0 == NULL == false`

.

## Booleans are Branching

Numbers are
function compositions.
Akin to that, booleans are computation branching.
Meaning, boolean operations are kind of building trees out of given data.
For example, here's how
`not`

operator looks like in LC:

So it takes a boolean and applies it to `false`

and `true`

.
If the boolean is `true`

, then it picks the `false`

branch, and vice versa.
A decision tree of sorts.
Some more elegant branching operations, `and`

& `or`

:

Again, branching operations instead of truth tables.

And here's how
`XOR`

looks like:

Simple and smart at the same time.

## Ah, Logic Again

But if we try to define operations like `nand`

or `nor`

, our branching doesn't help much.
Here's how
`nand`

might look like:

Not too bad, but it totally obviates the meaning of the operation.
It's just boolean juggling.
The truth tables I'm trying to avoid.
But the good thing about Lambda Calculus is that we can use the functions from before to fix that.
(Well, at least notation-wise—the compiled lambdas won't have names.
Still, most LC implementations allow for some `let`

/`where`

syntax, so I'm safe.)
This way derivative operations look much more meaningful:

We built our primitives out of branching functions. And then used them to define derivative operations. An elegant solution mapping boolean logic directly to LC. No truth tables, which is a win. Now to actually using these operations:

## Laconic If

Cutting it short, `if`

is:

Booleans already select between branches.
So `if`

doesn't need to do anything: just apply the boolean to both options.
And watch the magic happen.

A person familiar with the regular `if`

that chooses and runs only one branch:
"But that's dangerous: it's evaluating both branches. There might be side-effects."
There are no side-effects in LC, though.
And there is no "evaluation" in it either:
only reduction.
It's okay.

## Applying Booleans to Numbers

That's the part I've been teasing you with in the previous post. Finally, we can do comparisons and predicates on numbers. Here's the most basic—checking for zero:

Numbers are applying a function to a value.
If they do apply it even once (number > 0), return `false`

.
If they don't (number = 0), return
`true`

.
That simple.
And it doesn't matter if the number is arbitrarily big.
Applying `(λx.false)`

has no side-effects (again, there are none in LC).
So we can safely apply it as much as we wish to.

Comparisons, the simplest ones:

The number A is greater than another number B if subtracting B from A yield non-zero. (Notice that Church/Peano numerals are strictly non-negative. Smallest value subtraction can yield is zero.) And vice versa. Having these, other predicates are easy:

Maps the mental model perfectly: number A is greater if it's not less than or equal than B. And equality (this is slightly tricky) is when the numbers are both less than or equal than each other. Because if both are, then both must be the same number. Otherwise one of these will be greater and the condition will be false.

## Finally, Using Booleans!

The simplest example for usage of all these operators might be minimum and maximum functions:

I might've used
`leq`

here, but the behavior stays the same.
It doesn't matter which of the two we pick—both are valid numbers.
If they are the same number, they are fully
β-equivalent
and picking either is fine.
So it does not matter whether there's
`leq`

or `lt`

.

## Up Next: Consing Up The World

With that, I'm ready to conclude the booleans post. What happened here was:

- Booleans as decision trees.
- Branching logic operations.
- Perfect mapping from logic to LC for derived ops.
- Number predicates.
- And, finally, conditional operations on numbers (or any data, really—the idea is the same.)

With basic data types, numbers and booleans, one can already encode a lot. But there's a point at which one wants aggregate data types. Like cons trees/lists/structures. That's the next post topic, so byte (sic) your nails until then.