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).
true = λf.λx.f false = λf.λx.x
Coincidentally,
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
So it takes a boolean and applies it to
Again, branching operations instead of truth tables.
And here’s how
Simple and smart at the same time.
But if we try to define operations 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
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:
Cutting it short,
Booleans already select between branches.
So
A person familiar with the regular
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
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.
The simplest example for usage of all these operators might be minimum and maximum functions:
I might’ve used
With that, I’m ready to conclude the booleans post.
What happened here was:
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.
And maybe refresh your normal vs. applicative order knowledge.
Booleans are Branching
not = λp.p false true
and = λp.λq.p q p // If p is true, check q
or = λp.λq.p p q // If p is false, check q
// Alternative versions
and = λp.λq.p q false
or = λp.λq.p true q
xor = λp.λq.p (not q) q
Ah, Logic Again
nand = λp.λq.p (q false true) true
nand = λp.λq.(not (and p q))
nor = λp.λq.(not (or p q))
xnor = λp.λq.(not (xor p q))
Laconic If
if = λb.λt.λe.b t e
// Or even
if = λb.b
Applying Booleans to Numbers
iszero = λn.n (λx.false) true
leq = λm.λn.iszero (sub m n)
geq = λm.λn.iszero (sub n m)
gt = λm.λn.not (leq m n)
lt = λm.λn.not (geq m n)
eq = λm.λn.and (leq m n) (leq n m)
Finally, Using Booleans!
min = λm.λn.if (lt m n) m n
max = λm.λn.if (lt m n) n m
Up Next: Consing Up The World
Booleans as decision trees.