14 min read

Functional Programming has several advantages for better, more understandable and maintainable code. Currently, it’s popping up in many languages and frameworks, but let’s not dwell into the theory right now, and instead, let’s consider a simple problem, and how to solve it in a functional way.

[box type=”shadow” align=”” class=”” width=””]This is a guest post written by Federico Kereki, the author of the book Mastering Functional JavaScriptProgramming. Federico Kereki is a Uruguayan Systems Engineer, with a Masters Degree in Education, and more than thirty years’ experience as a consultant, system developer, university professor and writer.[/box]

Assume the following situation. You have developed an e-commerce site: the user can fill his shopping cart, and at the end, he must click on a BILL ME button, so his credit card will be charged. However, the user shouldn’t click twice (or more) or he would be billed several times.

The HTML part of your application might have something like this, somewhere:

<button id=“billButton” onclick=“billTheUser(some, sales, data)”>Bill me</button>

And, among the scripts you’d have something similar to this:

function billTheUser(some, sales, data) {
window.alert("Billing the user...");
// actually bill the user
}

(Assigning events handler directly in HTML, the way I did it, isn’t recommended. Rather, in unobtrusive fashion, you should assign the handlerthrough code. So… Do as I say, not as I do!)

This is a very barebone explanation of the problem and your web page, but it’s enough for our purposes. Let’s now get to think about ways of avoiding repeated clicks on that button… How can you manage to avoid the user clicking more than once?

We’ll consider several common solutions for this, and then analyze a functional way of solving the problem.OK, how many ways can you think of, to solve our problem? Let us take several solutions one by one, and analyze their quality.

Solution #1: hope for the best!

How can we solve the problem? The first so called solution may seem like a joke: do nothing, tell the user not to click twice, and hope for the best!

Caution button - functional programming

This is a weasel way of avoiding the problem, but I’ve seen several websites that just warn the user about the risks of clicking more than once, and actually do nothing to prevent the situation… the user got billed twice? we warned him… it’s his fault!

<button id="billButton" onclick="billTheUser(some, sales, data)">Bill me</button>
<b>WARNING: PRESS ONLY ONCE, DO NOT PRESS AGAIN!!</b>

OK, so this isn’t actually a solution; let’s move on to more serious proposals…

Solution #2: use a global flag

The solution most people would probably think of first, is using some global variable to record whether the user has already clicked on the button. You’d define a flag named something like clicked, initialized with false. When the user clicks on the button, you check the flag. If clicked was false, you change it to true, and execute the function; otherwise, you don’t do anything at all.

let clicked = false;
.
.
.
function billTheUser(some, sales, data) {
if (!clicked) {
clicked = true;
window.alert("Billing the user...");
// actually bill the user
}
}

This obviously works, but has several problems that must be addressed.

  • You are using a global variable, and you could change its value by accident.Global variables aren’t a good idea, neither in JS nor in other languages. (For moregood reasons NOT to use global variables, read http:/​/​wiki.​c2.​com/​?GlobalVariablesAreBad)
  • You must also remember to re-initialize it to false when the user starts buying again. If you don’t, the user won’t be able to do a second buy, because paying will have become impossible.
  • You will have difficulties testing this code because it depends on external things: that is, the clicked variable.

So, this isn’t a very good solution either… let’s keep thinking!

Solution #3: Remove the handler

We may go for a lateral kind of solution, and instead of having the function avoid repeated clicks, we might just remove the possibility of clicking altogether.

function billTheUser(some, sales, data) {
document.getElementById("billButton").onclick = null;
window.alert("Billing the user...");
// actually bill the user
}

This solution also has some problems.

  • The code is tightly coupled to the button, so you won’t be able to reuse it elsewhere;
  • You must remember to reset the handler; otherwise, the user won’t be able to make a second buy;
  • Testing will also be harder, because you’ll have to provide some DOM elements

We can enhance this solution a bit, and avoid coupling the function to the button, by providing the latter’s id as an extra argument in the call. (This idea can also be applied to some of the solutions below.) The HTML part would be:

<button
id="billButton"
onclick="billTheUser('billButton', some, sales, data)"
>
Bill me
</button>;

(note the extra argument) and the called function would be:

function billTheUser(buttonId, some, sales, data) {
document.getElementById(buttonId).onclick = null;
window.alert("Billing the user...");
// actually bill the user
}

This solution is somewhat better. But, in essence, we are still using a global element: not a variable, but the onclick value. So, despite the enhancement, this isn’t a very good solution too. Let’s move on.

Solution #4: Change the handler

A variant to the previous solution would be not removing the click function, and rather assign a new one instead. We are using functions as first-class objects here, when we assign the alreadyBilled() function to the click event:

function alreadyBilled() {
window.alert("Your billing process is running; don't click, please.");
}

function billTheUser(some, sales, data) {
document.getElementById("billButton").onclick = alreadyBilled;
window.alert("Billing the user...");
// actually bill the user
}

Now here’s a good point in this solution: if the user clicks a second time, he’ll get a pop-up warning not to do that, but he won’t be billed again. (From the point of view of the user experience, it’s better.) However, this solution still has the very same objections as the previous one (code coupled to the button, need to reset the handler, harder testing) so we won’t consider this to be quite good anyway.

Solution #5: disable the button

A similar idea: instead of removing the event handler, disable the button, so the user won’t be able to click:

function billTheUser(some, sales, data) {
document.getElementById("billButton").setAttribute("disabled", "true");
window.alert("Billing the user...");
// actually bill the user
}

This also works, but we still have objections as for the previous solutions (coupling the code to button, need to re-enable the button, harder testing) so we don’t like this solution either.

Solution #6: redefine the handler

Another idea: instead of changing anything in the button, let’s have the event handler change itself. The trick is in the second line; by assigning a new value to the billTheUser variable, we are actually dynamically changing what the function does! The first time you call the function, it will do its thing… but it will also change itself out of existence, by giving its name to a new function:

function billTheUser(some, sales, data) {
billTheUser = function() {};
window.alert("Billing the user...");
// actually bill the user
}

There’s a special trick in the solution. Functions are global, so the line billTheUser=…actually changes the function inner workings; from that point on, billTheUser will be a new (null) function. This solution is still hard to test. Even worse, how would you restore the functionality of billTheUser, setting it back to its original objective?

Solution #7: use a local flag

We can go back to the idea of using a flag, but instead of making it global (which was our main objection) we can use an Immediately Invoked Function Expression (IIFE) that, via a closure, makes clicked local to the function, and not visible anywhere else:

var billTheUser = (clicked => {
return (some, sales, data) => {
if (!clicked) {
clicked = true;
window.alert("Billing the user...");
// actually bill the user
}
};
})(false);

In particular, see how clicked gets its initial false value, from the call, at the end. This solution is along the lines of the global variable solution, but using a private, local variable is an enhancement. The only objection we could find is that you’ll have to rework every function that needs to be called only once, to work in this fashion. (And, as we’ll see below, our FP solution is similar in some ways to it.) OK, it’s not too hard to do, but don’t forget the Don’t Repeat Yourself (D.R.Y) advice!

A functional, higher order, solution

Now that we’ve gone through several “normal” solutions, let’s try to be more general: after all, requiring that some function or other be executed only once, isn’t that outlandish, and may be required elsewhere! Let’s lay down some principles:

  • The original function (the one that may be called only once) should do that thing, and no other;
  • We don’t want to modify the original function in any way; so,
  • We need to have a new function, that will call the original one only once, and
  • We want a general solution, that we can apply to any number of original functions.

(The first principle listed above is the single responsibility principle (the S in S.O.L.I.D.) which states that every function should be responsible over a single functionality. For more on S.O.L.I.D., check the article by “Uncle Bob” (Robert C. Martin, who wrote the five principles) at http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod).

Can we do it? Yes; and we’ll write a higher order function, which we’ll be able to apply to any function, to produce a new function that will work only once. Let’s see how!

If we don’t want to modify the original function, we’ll create a higher order function, which we’ll, inspiredly, name once(). This function will receive a function as a parameter, and will return a new function, which will work only a single time.

(By the way, libraries such as Underscore and LoDash already have a similar function, invoked as _.once(). Ramda also provides R.once(), and most FP libraries include similar functionality, so you wouldn’t actually have to program it on your own.)

Our once() function way seem imposing at first, but as you get accustomed to working in FP fashion, you’ll get used to this sort of code, and will find it to be quite understandable:

const once = fn => {
let done = false;
return (...args) => {
if (!done) {
done = true;
fn(...args);
}
};
};

Let’s go over some of the finer points of this function:

  • The first line shows that once() receives a function (fn) as its parameter.
  • We are defining an internal, private done variable, by taking advantage of a closure, as in Solution #7, above. We opted not to call it clicked, as earlier, because you don’t necessarily need to click on a button to call the function; we went for a more general term.
  • The line return (…args) => … says that once() will return a function, with some (0, 1, or more) parameters. We are using the modern spread syntax; with older versions of JS you’d have to work with the arguments object; see https:/​/developer.​mozilla.​org/​en/​docs/​Web/​JavaScript/​Reference/​Functions/arguments for more on that. The ES8 way is simpler and shorter!
  • We assign done = true before calling fn(), just in case that function throws an exception. Of course, if you don’t want to disable the function unless it has successfully ended, then you could move the assignment just below the fn() call.
  • After that setting is done, we finally call the original function. Note the use of the spread operator to pass along whatever parameters the original fn() had.

So, how would we use it? We don’t even need to store the newly generated function in any place; we can simply write the onclick method, as shown as follows:

<button id="billButton" onclick="once(billTheUser)(some, sales, data)">
Bill me
</button>;

Pay close attention to the syntax! When the user clicks on the button, the function that gets called with (some, sales, data) argument isn’t billTheUser(), but rather the result of having called once() with billTheUser as a parameter. That result is the one that can be called only a single time.

We can run a simple test:

const squeak = a => console.log(a + " squeak!!");
squeak("original");        // "original squeak!!"
squeak("original");        // "original squeak!!"
squeak("original");        // "original squeak!!"
const squeakOnce = once(squeak);
squeakOnce("only once");   // "only once squeak!!"
squeakOnce("only once");   // no output
squeakOnce("only once");   // no output

Some varieties

In one of the solutions above, we mentioned that it would be a good idea to do something every time after the first, and not silently ignoring the user’s clicks. We’ll write a new higher order function, that takes a second parameter; a function to be called every time from the second call onward:

const onceAndAfter = (f, g) => {
let done = false;
return (...args) => {
if (!done) {
done = true;
f(...args);
} else {
g(...args);
}
};
};

We have ventured further in higher-order functions; onceAndAfter() takes two functions as parameters and produces yet a third one, that includes the other two within.

We could make onceAndAfter() more flexible, by giving a default value for g, along the lines of const onceAndAfter = (f, g = ()=>{})… so if you didn’t want to specify the second function, it would still work fine, because it would then call a do-nothing function.

We can do a quick-and-dirty test, along with the same lines as we did earlier:

const squeak = () => console.log("squeak!!");
const creak = () => console.log("creak!!");
const makeSound = onceAndAfter(squeak, creak);
makeSound(); // "squeak!!"
makeSound(); // "creak!!"
makeSound(); // "creak!!"
makeSound(); // "creak!!"

So, we got an even more enhanced function! Just to practice with some more varieties and possibilities, can you solve these extra questions?

  • Everything has a limit! As an extension ofonce(), could you write a higherorder function thisManyTimes(fn,n) that would let you call fn() up to n times, but would afterward do nothing? To give an example, once(fn) and thisManyTimes(fn,1) would produce functions that behave in exactly the same way.
  • Alternating functions. In the spirit of ouronceAndAfter()function, could youwrite an alternator() higher-order function that gets two functions as arguments, and on each call, alternatively calls one and another? The expected behavior should be as in the following example:
let sayA = () => console.log("A");
let sayB = () => console.log("B");
let alt = alternator(sayA, sayB);
alt(); // A
alt(); // B
alt(); // A
alt(); // B
alt(); // A
alt(); // B

To summarize, we’ve seen a simple problem, based on a real-life situation, and after analyzing several common ways of solving that, we chose a functional thinking solution. We saw how to apply FP to our problem, and we also found a more general higher order way that we could apply to similar problems, with no further code changes. Finally, we produced an even better solution, from the user experience point of view.

Functional Programming can be applied all throughout your code, to enhance and simplify your development. This has been just a simple example, using higher order functions, but FP goes beyond that and provides many more tools for better coding.

[author title=”Author Bio”]The author, Federico Kereki, is currently a Subject Matter Expert at Globant, where he gets to use a good mixture of development frameworks, programming tools, and operating systems, such as JavaScript, Node.JS, React and Redux, SOA, Containers, and PHP, with both Windows and Linux. He has taught several computer science courses at Universidad de la República, Universidad ORT Uruguay, and Universidad de la Empresa, he has also written texts for these courses.

Federico has written several articles—on JavaScript, web development, and open source topics—for magazines such as Linux Journal and LinuxPro Magazine in the United States, Linux+ and Mundo Linux in Europe, and for web sites such as Linux and IBM Developer Works. Kereki has given talks on Functional Programming with JavaScript in public conferences (such as JSCONF 2016) and used these techniques to develop Internet systems for businesses in Uruguay and abroad.[/author]

Read More

Introduction to the Functional Programming

What is the difference between functional and object oriented programming?

Manipulating functions in functional programming

 

LEAVE A REPLY

Please enter your comment!
Please enter your name here