Using Minimal Functions To Build Predicates And Boolean Operators
In the last exciting episode of the Mindshift blog, we looked at how subtraction can be performed when our counting system only allows us to count upwards from zero.
Now we need to develop some predicate functions to perform various equality or equivalence tests, and from these, then build up a wider range of functional tools.
There’s Nothing There - Or Is There?
It would be pretty handy if we could determine whether a quantity function is
ZERO or not. To put that in fancier language, how can we tell whether or not a quantity function has zero magnitude?
Lets remind ourselves of the basic definition of
ONE (ignoring the use of the
Notice how the transform function is used inside the
ZERO function – it isn’t. It’s completely ignored; all that happens inside
ZERO is that the second parameter (
start_value) is returned unmodified.
Let’s hold the definition of
ZEROup against the definition of another function we’ve seen already –
Ok, the parameter names are different, but their functionality is identical! Both
FALSEperform no processing (in the sense of performing some calculation) and brainlessly return their second parameter.
This now provides us with a (possibly unexpected) way to demonstrate that it is far more than simply computing convention to evaluate Boolean false to zero – they are in fact functionally indistinguishable.
The only circumstance under which a quantity function will ignore its transformation function is when it has a magnitude of zero. The flip-side of this that for any quantity function having a non-zero magnitude, the transformation function will always be called at least once.
Therefore, this behaviour can be used to get a quantity function to reveal whether or not it has zero magnitude.
We will create a special transformation function that, if called, will always return
FALSE. Now, when we pass the quantity function both this special transformation function and a starting value of
TRUE, we know that if the quantity function has a magnitude of zero, the transformation function will be ignored and we'll get back the value
TRUE. For any other magnitude, the transformation function will be called at least once, thus guaranteeing a return value of
So now that we can determine whether a quantity function has a magnitude of zero, we can now define the “less than or equal to” predicate.
Less Is More (Than a Negative Number)
You might recall from the previous blog that our counting system cannot handle negative numbers. This means that we will get odd results such as
5 - 8 = 0 instead of the expected
Apart from limiting all our calculations to give positive answers, this is in fact a useful feature that allows us to check whether one number is greater than another number.
All we need to do is subtract one number from the other, then pass the result to our new
IS_ZERO function. And there you have it, a definition for the “less than or equal to” predicate.
Using the Boolean values of
FALSE, the normal Boolean operators of
XOR can be defined.
Now that we have a
NOT and a
IS_LTE operator, it is quite straight-forward to adapt this to create a “less than” operator.
Now that we have these tools available to us, we can start to look at arithmetic operations such as division and modulus. However, what makes these operations harder to implement is that they need to perform the
SUBTRACT operation an unknown number of times. This requires the use of recursion.
Here, we run up against another problem. Any recursively defined function must be able to make reference to itself; but if we create a
DIV function that refers to itself directly by name, then we have broken our rule disallowing the use of external names for self-reference.
I know I’ve been cheating slightly by assigning names such as
MULTIPLYto our function definitions, but this is to account for our severely limited human ability to interpret long expressions. Really, names such as
MULTIPLY behave as macros that are substituted at runtime for the functions they represent.
At first, you might think this arbitrary restriction requires us to perform some pointless mental gymnastics simply to overcome our own self-imposed hurdles. Whilst this might be true from one particular perspective, from the wider perspective of understanding the nature of computation, overcoming this hurdle leads us to an understanding of a very profound construct in functional programming, known as the "Fixed Point" or Y-combinator.
All source code from the Mindshift blog series can be found on GitHub