// Increment a number
incr = n => n + 1
Here, we assign an anonymous function to the name incr. This function receives a parameter n and returns the value n+1. Ok, but that’s not exactly a ground breaking stuff is it? Nonetheless, at least this arrow function meets our requirements: it takes one parameter and contains one expression. Using this minimal definition of an incr function, let’s now try to perform the most basic task possible with integers – counting through the natural numbers.
Well, OK. It’s a pretty clumsy way of counting, but I suppose it works. This is certainly neither elegant nor optimised; but, we what we have done is create a solution so simple that it obviously contains no errors. Just by looking at the definition of the incr function, and then at how this function is being used, we can easily determine that, in spite of the awkward usage, it is completely correct. Incidentally, the above nested calls to incr are exactly how the Clojure function iterate works. So we have created a software design that is "simple" according to Tony Hoare’s definition of the word. In this discussion, computational efficiency is not the issue. First and foremost, we want to write code that is correct; we’ll deal with the question of optimisation later. Is That As Simple As We Can Make It? It would be easy to stop at this point and think that since we have found a simple way of deriving the counting numbers, that we have therefore found the simplest way. However, as I said above, in order to move forward here, we need to be willing to examine our own thought processes – and then not run away from the discoveries that such an examination might reveal! In the above coding, we have created a situation in which a function is passed a starting value, and then using a sequence of nested calls, the result is passed into the same function multiple times in order to derive the required value. However, there is a problem with this design: in addition to the very clumsy usage, we have no way to derive the integer zero – at least not without resorting to the messy workaround of passing in a starting value of -1. And since computers start counting from zero instead of one, this is quite an omission. We need to look at how we can fix this. We can generalise the way the incr function is being used here by seeing it as the internal workings of a larger, more general purpose function that takes two parameters: The first parameter is a function that when called, transforms some starting value The second parameter is the value to be transformed Notice what we have not said here. We have not said that we will start from zero and then perform the transformation “plus one” on that value. Instead, we are describing a much more general purpose form of functionality. Our general purpose function receives two things: Some arbitrary starting value Some transformation function that can manipulate the start value. The transformation function is then applied a certain number of times to the start value in order to derive an answer. In order to use our general purpose function for counting, we must supply a value and a function that will generate the required result. So in the case of countring, we must supply: A start value of zero A transformation function capable of performing the “add one” operation Now our general purpose function has been "configured" (for want of a better term) to derive the counting numbers. Deriving Zero Well that’s OK for deriving the integers 1 or 2 or 3, but how can we derive 0? Remember that this general purpose function is designed to apply the transformation function a certain number of times to some starting value? Q: If our starting value is zero, then how many times should we apply the transformation function? A: Oh, that's easy - zero times! However, before we go charging ahead and create a function that takes two parameters, we must remember our self-imposed restriction that a function may only take one parameter. Hmmm, what to do… Currying and Partial Functions In order to create a function that takes two parameters, yet does not violate our self-imposed constraint that functions may only take one parameter, we need to perform a process called “Currying” (named after the logician Haskell Curry). Currying is a process in which a function that initially takes multiple parameters is refactored into a sequence of partial functions that each take a single parameter. Lets start with a function that takes two parameters. In order for this function to behave correctly, it must receive both parameter values at the time you call it.
(parm1, parm2) => parm2 + parm1
Let's pretend we are able to call this function, but pass it only one parameter - what would we end up with? If we set parm1 equal to 1 and then rewrite the function, we'd end up with:
parm2 => parm2 + 1
In other words, we get back another function in which the value of parm1 has been substituted with the supplied value, but the value of parm2 remains unknown. So we can rewrite the original function like this:
parm1 => parm2 => parm2 + parm1
This refactoring process is known as currying and the function is now said to have been curried. We now have the ability to create partial functions. A partial function is a function that has been supplied with some, but not all of its parameters. So let’s apply this understanding to the incr function. This function does nothing more than take a single parameter and add one to it. So in fact incr is nothing more than a special case of the addition operation where one of the operands has been hard-coded to 1. So lets create a generic adder function (no, not the snake…)
adder = val1 => val2 => val2 + val1
Ok, that looks simple enough, but how do we use it? Well, in the case of our incr function, we know that one of the values in our addition operation will always be the number 1. Therefore, if we call adder with only one parameter (the number 1), then we will get back a partial function that takes a single parameter and then adds one to it.
increment = adder(1)
“So what?” you might be saying, “Aren’t we just back to where we started?“. True enough, our new increment function behaves no differently than the original incr function before. But we have now analysed the problem right down to bare metal. We have recognised that in order to increment a value, we need to perform the arithmetic operation “add” where one of the parameters has been hard-coded to 1. In other words, we’ve followed Tony Hoare’s recommendation and produced a software system (albeit a trivial one) so simple that is obviously contains no errors. The point of partial functions is that they are designed to perform most, but not all of an operation. In this case, we have created an incr function by passing the general purpose adder function only one of its two required parameters. This results in a function that can add one to any subsequent value it receives. We will soon see that partial functions are very important and useful building blocks in functional programming. Generating the Counting Numbers(?) So now that we have a way to increment a value, we can create a set of functions that can calculate any of the counting numbers. In the previous section, we saw that any counting number can be generated by transforming a starting value a certain number of times. We also described the fact that if we want a function that generates the quantity zero, all we need to do (or not do) is apply the transformation function zero times. So we can now create some functions that generate the first few counting numbers:
ZERO = transform => start_value => start_value
ONE = transform => start_value => transform(start_value)
TWO = transform => start_value => transform(transform(start_value))
You can think of the function names ZERO, ONE and TWO as representing the number of times the transformation function is applied to the starting value. So if we supply increment as the transformation function and 0 as the starting value, then we have created a means of generating the counting numbers.
Now, put your brain into deep think mode and ask yourself this question… What exactly, do the functions ZERO, ONE, and TWO do? At first glance you might think, “Oh they generate the counting numbers”. Well yes - but that's only the superficial answer… Quantity Functions Above we saw that you should think of the names of these functions as representing the number of times the transformation function is applied to the starting value – and this is the key to understanding that these functions can do a lot more than simply generate counting numbers. Up until now, we have only ever supplied the transformation function increment and a starting value of zero, so it is only under these circumstances that can we generate the counting numbers. Lets illustrate this with some practical examples. Instead of passing TWO a starting value of zero, let’s pass it some other integer.
// Increment 5 using the function TWO
So here, function TWO applies the increment function twice to the starting value of 5; hence the result 7. But we could equally well create a decrement function and apply that to the value 5 like this:
decrement = adder(-1)
// Increment the character 'A' using the function TWO
// Concatenate a character to the end of a string
// This function needs to remember the character being concatenated,
// so we wrap the concat() function call within a partial function
// that can remember the value of "char".
// "acc" is the accumulator into which the characters are
str_concatenate = char => acc => acc.concat(char)
To make use of str_concatenate, we must tell it which character is to be concatenated. In this case, we’ll arbitrarily choose the plus character ‘+’. Now, we can call our functions again, but instead of using increment as the transformation function and a starting value of 0, we will perform our transformation using str_concatenate('+') and a starting value of the empty string…
Now we can begin to see that the functions ZERO, ONE and TWO are much more versatile than simply a means for generating the counting numbers. They are generic functions that transform some starting value a given number of times. In other words, these functions can do much more than generate the integers 0, 1, and 2; instead, they implement the concept of a quantity such as zero-ness or one-ness or two-ness. Now using this somewhat more abstract idea of a quantity function, we can apply any transformation function to any starting value a given number of times. I trust this gives you the start of an insight into both the subtlety and power of the functional approach to programming. In the next Mindshift blog, we’ll develop the idea of how these abstract functions can be used to perform basic arithmetic operations. Enjoy! Chris W