Mindshift: Part 7

Ordering a Takeaway

Subtraction, That Is, Not Fish & Chips…

In the previous Mindshift blog, we looked at how minimal JavaScript arrow functions can be used to define firstly a PAIR of values, and secondly how extracting the first or second item from such a PAIR yields a definition of the Boolean values of TRUE and FALSE.

So now that we have these definitions under our belt, we can look at how subtraction is implemented.

In the same way that the addition of a and b is defined as the b’th successor of a, so subtracting b from a amounts to nothing more than finding the b’th predecessor of a.

So the task for this blog is to develop a predecessor function1 and then from that, develop a SUBTRACT function.

Counting Backwards, Or Not…

First however, we must understand the constraints of our current counting system.

  1. We always start counting from zero
  2. We can only count upwards. I.E. this system provides no way to represent negative number

This does restrict the functionality of our SUBTRACT function, but that’s not an issue here for two reasons:

  1. We said we are only going to define the counting numbers, and by definition, these are the positive integers.
  2. Our primary focus is to understand the use of abstract functions, not create a full mathematical toolbox.

So given that our implementation cannot actually count backwards, we will have to think of the predecessor of a number in a different way.

The way we do can this is to think of numbers as pairs such as (n-1,n). Then, to derive the predecessor of a number, we start counting up from zero – but always keeping track of the numbers as pairs. This means that when we’ve finished counting up to n (the second number in our pair), the required predecessor will be the first number of the pair.

In the previous blog, we saw how the PAIR function operates, so now we need to apply that function to our situation.

Counting in Pairs

What we need now is a function that creates not just the successor of a single quantity function, but creates the successors of a PAIR of quantity functions. So what we need here is a SUCC_PAIR function.

Here, we will implement the “shift and increment” principle. A new pair of numbers is constructed as follows:

  • The first number in the new pair is the second number from the old pair
  • The second number of the new pair is the successor of the second number of the old pair.
// Abstract function to represent any two things and a function to apply
// to them

var PAIR = val1 => val2 => fn => fn(val1)(val2);

// Abstract functions representing TRUE and FALSE
var TRUE  = true_part => false_part => true_part;
var FALSE = true_part => false_part => false_part;

// Create a new version of the successor function that can operate on a
// pair of quantity functions.
// This function receives PAIR(n-1)(n) and performs a "shift and
// increment" operation in order to return PAIR(n)(n+1)

var SUCC_PAIR = pair => PAIR(pair(FALSE))(SUCC(pair(FALSE)));

Now let’s use this function to create the next two successor pairs to PAIR(ZERO)(ZERO) – which we expect to be PAIR(ZERO)(ONE) and PAIR(ONE)(TWO).

// Starting from PAIR(ZERO)(ZERO), create the next two successive pairs.
var p00 = PAIR(ZERO)(ZERO);
var p01 = SUCC_PAIR(p00);
var p12 = SUCC_PAIR(p01);

to_integer(p01(TRUE));    // -> 0
to_integer(p01(FALSE));   // -> 1

to_integer(p12(TRUE));    // -> 1
to_integer(p12(FALSE));   // -> 2

When Did Last See Your Father – Or Any Other Predecessor?

Lastly, to find the predecessor of any particular number n, we need to invoke the SUCC_PAIR function n times on the PAIR(ZERO)(ZERO), and the required predecessor will then be the first number of the resulting pair.

Do you remember how a quantity function works? A quantity function applies the transformation function to the starting value as many times as is represented by that quantity function.

Q: What’s our transformation function going to be?
A: The SUCC_PAIR function.

Q: What’s our starting value going to be?
A: We will define our starting point for counting to be PAIR(ZERO)(ZERO).

This means that the predecessor of any particular quantity function can be obtained by a two-step process: first, pass SUCC_PAIR and PAIR(ZERO)(ZERO) to the quantity function as the transformation function and starting value, and second, extract the first value from the resulting pair.

// Create a PREDecessor function by using SUCC_PAIR as the transformation
// function, and PAIR(ZERO)(ZERO) as the starting value. This will return
// a number PAIR where we are interested only in the first number.
// Therefore, pass the resulting pair to TRUE in order to extract the
// required value.

var PRED = qtyFn => qtyFn(SUCC_PAIR)(PAIR(ZERO)(ZERO))(TRUE);

PRED(EIGHT);              // -> The quantity function SEVEN
to_integer(PRED(EIGHT));  // -> 7

Phew! That was a lot of hard work just to create a predecessor function, but finally we’re in a position to define a SUBTRACT function.

Subtraction Engine

Since subtraction is non-commutative, we must stipulate that SUBTRACT always takes the second quantity away from the first; but apart from that, we’re using exactly the same principle as has been used before. In other words, to subtract b from a, apply the PRED function b times starting from a.

// Create a SUBTRACT function by applying the PRED function n times to the
// start value: where n is the second parameter and start_value is the
// first parameter.

var SUBTRACT = qtyFn1 => qtyFn2 => qtyFn2(PRED)(qtyFn1);

SUBTRACT(EIGHT)(FIVE);             // -> the quantity function THREE
to_integer(SUBTRACT(EIGHT)(FIVE)); // -> 3
to_integer(SUBTRACT(FIVE)(EIGHT)); // -> 0 Uh??
// Our counting system can't handle negative numbers, so this is the best
// it can do

Now that we have the ability to perform any subtraction whose answer is greater than or equal to zero2, we can start to look at other arithmetic operations such as modulus and division; but, as always, there’s a catch – in fact there are two catches caused by the fact that both the modulus and division operators are implemented using repeated subtraction:

  1. We must perform the SUBTRACT operation until the divisor becomes bigger than the remainder – yet, we have no functions such as IS_ZERO or IS_LTE (is less than or equal to).
  2. Then there’s the thornier problem that since we have no idea how many times to the SUBTRACT function will need to be called, it must called recursively.

Teeny weeny problem… Our ground rules state that all functions must be anonymous. Therefore, how can the function SUBTRACT call itself without referring to it by name?

This is where something called the Y-Combinator comes in; but more of that later.

Chris W


Footnotes

1) The definition of the predecessor function shown in this blog is not the only way this functionality could be implemented, but it is the simplest to understand. The following definition of the predecessor function is also widely used:

var PRED = n => f => x => n(g => h => h(g(f)))(y => x)(y => y);

Whilst this definition is equally valid, it is much harder to understand because it involves the use of nested wrapper functions – so we won’t look at this implementation here.

2) It might seem ridiculous to you that we have created a SUBTRACT function that yields zero when asked what 5 - 8 is. But this is not so wacky as it might seem. For more information on this type of operation, look up Monus – but be warned – you’ll be venturing into the lands of Abstract Algebra and Group Theory. Here be dragons.

Advertisements

2 thoughts on “Mindshift: Part 7

Comments are closed.