• So You Want to be a Functional Programmer (Part 2)
  • The Nuggets translation Project
  • Translator: Airmacho
  • Proofreader: Cyseria, Tina92

To understand functional programming, the first step is always the most important and difficult. But with the right mindset, it’s not too difficult.

Previous part: Part 1

Helpful hints

Please read the code carefully and make sure you understand it before continuing. Each code paragraph is based on the code that preceded it.

If you hurry, you may miss out some important details.

refactoring

Let’s start by refactoring some JavaScript code:

function validateSsn(ssn) {
    if (/^\d{3}-\d{2}-\d{4}$/.exec(ssn))
        console.log('Valid SSN');
    else
        console.log('Invalid SSN');
}

function validatePhone(phone) {
    if (/^\(\d{3}\)\d{3}-\d{4}$/.exec(phone))
        console.log('Valid Phone Number');
    else
        console.log('Invalid Phone Number');
}
Copy the code

We’ve all written code like this before, and after a while we’ll see that the above two functions are actually the same, with minor differences (bold highlighting).

Instead of creating validatePhone by copying, pasting, and modifying the validateSsn function, we should create a separate function that parameterizes the above differences.

In this case, we can abstract the parameters to be validated, the regular expression to be validated, and the printed text to be passed to the method as parameters.

Refactored code:

function validateValue(value, regex, type) {
    if (regex.exec(value))
        console.log('Invalid ' + type);
    else
        console.log('Valid ' + type);
}
Copy the code

The SSN, phone parameters that were validated in the old code are now represented by the value parameter.

Regular expression /^\d{3}-\d{2}-\d{4}/ with variable regex.

Finally, the text ‘SSN’ and ‘Phone Number’ to be printed are concatenated with the variable type.

One function is much better than two, or worse, three, four, ten functions. This keeps your code clean and easy to maintain.

For example, if there is a bug in your code, you only need to fix it in one place, rather than looking through the entire code base for every place where the code has been pasted or modified.

But when you have a situation like this:

function validateAddress(address) {
    if (parseAddress(address))
        console.log('Valid Address');
    else
        console.log('Invalid Address');
}

function validateName(name) {
    if (parseFullName(name))
        console.log('Valid Name');
    else
        console.log('Invalid Name');
}
Copy the code

Here both the parseAddress and parseFullName functions take a single string argument and return true if parsing conditions are met.

How do we refactor this code?

We can replace address and name with value, and replace ‘address’ and ‘name’ with type, just like we did before, but passing in regular expressions as arguments, now functions.

It would be nice if we could pass in a function as an argument…

Higher-order functions

Many languages do not support passing functions as arguments. Some languages are supported, but not intuitive to use.

In functional programming, functions are the first citizens of the language. In other words, a function is just another value.

Since functions are values, we can pass them into functions as arguments.

Although JavaSscript is not a purely functional language, you can use it to do some functional operations. We can reconstitute the previous two functions into a function called parseFunc, passing the parseFunc function as an argument:

function validateValueWithFunc(value, parseFunc, type) {
    if (parseFunc(value))
        console.log('Invalid ' + type);
    else
        console.log('Valid ' + type);
}
Copy the code

Our new function is the higher-order function.

Higher-order functions can either accept functions as arguments, return functions as return values, or satisfy both conditions.

Now we can abstract the previous four functions into a higher-order function (we can do this in JavaScript because regx.exec returns true if the re matches) :

validateValueWithFunc('123-45-6789', /^\d{3}-\d{2}-\d{4}$/.exec, 'SSN');
validateValueWithFunc('(123)456-7890', /^\(\d{3}\)\d{3}-\d{4}$/.exec, 'Phone');
validateValueWithFunc('123 Main St.', parseAddress, 'Address');
validateValueWithFunc('Joe Mama', parseName, 'Name');
Copy the code

This is much better than using four nearly identical functions before.

But watch out for regular expressions. They’re also a little wordy. Now let’s refactor the code to tidy things up:

var parseSsn = /^\d{3}-\d{2}-\d{4}$/.exec;
var parsePhone = /^\(\d{3}\)\d{3}-\d{4}$/.exec;

validateValueWithFunc('123-45-6789', parseSsn, 'SSN');
validateValueWithFunc('(123)456-7890', parsePhone, 'Phone');
validateValueWithFunc('123 Main St.', parseAddress, 'Address');
validateValueWithFunc('Joe Mama', parseName, 'Name');
Copy the code

Much better. Now if we want to check if a value is a phone number, we don’t have to copy and paste the regular expression.

But imagine that we have more regular expressions to match than parseSsn and parsePhone. Each time we create a new function, we use a regular expression and call.exec. Trust me, it’s easy to miss.

We can solve this problem by creating another higher-order function and calling exec internally:

function makeRegexParser(regex) {
    return regex.exec;
}

var parseSsn = makeRegexParser(/^\d{3}-\d{2}-\d{4}$/);
var parsePhone = makeRegexParser(/^\(\d{3}\)\d{3}-\d{4}$/);

validateValueWithFunc('123-45-6789', parseSsn, 'SSN');
validateValueWithFunc('(123)456-7890', parsePhone, 'Phone');
validateValueWithFunc('123 Main St.', parseAddress, 'Address');
validateValueWithFunc('Joe Mama', parseName, 'Name');
Copy the code

Here, makeRegexParser takes a regular expression as an argument and returns an exec function that takes the validated string as an argument. ValidateValueWithFunc can pass strings, values, to parse functions such as exec.

ParseSsn and parsePhone are just as usable as the exec function with regular expressions.

True, this is only a slight improvement, but this shows us an example of a higher-order function returning a function as a return value.

But you can imagine how this would have benefited us if makeRegexParser had been more complex.

Here is another example of a higher-order function returning a function as a return value:

function makeAdder(constantValue) {
    return function adder(value) {
        return constantValue + value;
    };
}
Copy the code

Here the makeAddr function takes a parameter constantValue and returns a function addr, which returns the sum of the contantValue plus any value it accepts.

Its usage is:

var add10 = makeAdder(10);
console.log(add10(20)); // prints 30
console.log(add10(30)); // prints 40
console.log(add10(40)); // prints 50
Copy the code

We create the add10 function by passing 10 as an argument to makeAddr, which takes an arbitrary value as an argument and returns a sum with 10.

Note that even after makeAddr returns, the function addr can still get the value of the constantValue parameter. This is because constantValue is in scope when the ADDR function is created.

This behavior is important because otherwise, a function that returns a function as a return value won’t be much use. So it’s really important that we understand how it works.

This behavior is called closure.

closure

Here’s a function that deliberately uses closures:

function grandParent(g1, g2) {
    var g3 = 3;
    return function parent(p1, p2) {
        var p3 = 33;
        return function child(c1, c2) {
            var c3 = 333;
            return g1 + g2 + g3 + p1 + p2 + p3 + c1 + c2 + c3;
        };
    };
}
Copy the code

In this example, the child function can get variable values defined in its own, parent, and grandParent scopes.

The parent function can get the values of variables defined by itself and the grandParent function scope.

GrandParent only gets its own variables (see the pyramid diagram above for clarity).

Here’s an example:

var parentFunc = grandParent(1, 2); // returns parent()
var childFunc = parentFunc(11, 22); // returns child()
console.log(childFunc(111, 222)); // prints 738
// 1 + 2 + 3 + 11 + 22 + 33 + 111 + 222 + 333 == 738
Copy the code

Here, parentFunc can keep the parent function scoped because grandParent returns parent as the return value.

Similarly, childFunc can preserve the scope of the child function because parentFunc is the parent function that returns the child function.

When a function is created, all variables of the scope in which it was created are readable. If the function is still referenced, the scope remains alive. For example, the scope of the child function survives as long as a reference to childFunc exists.

Closures are functions that keep their scope alive by being referenced.

Note that in JavaScript, closures can introduce problems because variables are mutable. For example, these variables may be modified from the time they are closed to the time the function returns.

Fortunately, variables in functional languages are immutable, so this common error and confusion can be eliminated.

My brain!

That’s enough to digest for now.

In the rest of the article, I will cover function composition, Currization, and functions common in functional programming (map, filter, fold, etc.).

Next: [Part 3]