+6

🤔So You Want to be a Functional Programmer (Part 2)🤓

The first step to understanding Functional Programming is the hardest, but it doesn't have to be if you have the right attitude.

Previous parts: Part 1

Friendly Reminder

image.png

Read the code carefully and take your time. Make sure you understand it before continuing. Each part of the code builds on the part before it, so if you rush you may miss something important.

Refactoring

image.png

Let’s think about refactoring for a minute. Here’s 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');
}

We have written code that is very similar before, but with a few small changes. Instead of copying and pasting the code and then changing it, we should create one function and change the parts that are different. In this example, we would change the value, the regular expression, and the message printed.

The refactored code:

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

The old code had two separate functions for Social Security Numbers (ssn) and phone numbers. Now, these two functions have been combined into one, which makes the code easier to maintain. If there is a bug, you only have to fix it in one place instead of searching through the whole codebase to find where the function may have been pasted and modified.

But what happens when you have the following situation:

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");
}

We can refactor this by using a value for address and name, and type for 'Address' and 'Name', and replacing the regular expression with a function that takes a string as a parameter and returns true if it parses.

Higher-Order Functions

image.png

Some programming languages do not allow you to give a function as an input to another function. Some languages do allow it, but it is not simple to do.

In Functional Programming, a function is a first-class citizen of the language. In other words, a function is just another value.

We can use a single function to do the same thing as two functions by passing one of the functions as a parameter to the other. Even though Javascript is not a purely functional language, we can still use some functional operations with it.

function validateValueWithFunc(value, parseFunc, type) {
  if (parseFunc(value)) console.log("Invalid " + type);
  else console.log("Valid " + type);
}

Our new function is called a Higher-order Function.

Higher-order Functions either take functions as parameters, return functions or both.

We can use a higher-order function to check if the four previous functions have found a match. This works in Javascript because if Regex.exec finds a match, it will return a value that is considered to be true.

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");

It is much better to have one function instead of four that are almost the same. However, the regular expressions used are quite long. We can make our code simpler by taking out the regular expressions.

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");

We have improved the way we parse phone numbers so that we don't have to copy and paste the regular expression each time. However, if we have more regular expressions to parse, we need to remember to add .exec to the end of each one, which can be easy to forget.

We can protect ourselves from this by making a special function that gives us the exec function when we call it.

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");

makeRegexParser takes a regular expression and returns a function, exec, which takes a string. validateValueWithFunc will use this exec function to check if the string, value, matches the regular expression. parseSsn and parsePhone are similar to before, but now use the exec function from makeRegexParser. This is a small improvement, but if makeRegexParser was more complex, it would be more beneficial.

Here’s another example of a higher-order function that returns a function:

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

Here we have makeAdder that takes constantValue and returns adder, a function that will add that constant to any value it gets passed.

Here’s how it can be used:

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

We create a function, add10, which adds 10 to any number we give it. This is possible because when we create the function, add10, it has access to the constant value 10, even after the function makeAdder returns.

This behavior is called a Closure.

Closures

image.png

Here’s a contrived example of functions that use 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;
    };
  };
}

The child can see the variables of itself, its parent, and its grandparent. The parent can see the variables of itself and its grandparent. The grandparent can only see its own variables.

Here’s an example of its use:

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

When a function is created, all of the variables in its scope at the time of creation are accessible to it for as long as the function exists. The function will exist as long as there is still a reference to it.

For example, the child's scope will exist as long as childFunc still references it. parentFunc and childFunc keep their respective scopes alive since they both return the parent and child respectively.

A closure is a function’s scope that’s kept alive by a reference to that function.

In Javascript, it can be difficult to use closures because the variables can be changed between the time they are closed over and the time the returned function is used. However, in functional languages, the variables are not able to be changed, which prevents potential problems and confusion.

My Brain!!!!

image.png

Enough for now.

In subsequent parts of this article, I’ll talk about Functional Composition, Currying, common functional functions (e.g map, filter, fold etc.), and more.

Up Next: Part 3 (coming soon...)

Mình hy vọng bạn thích bài viết này và học thêm được điều gì đó mới.

Donate mình một ly cafe hoặc 1 cây bút bi để mình có thêm động lực cho ra nhiều bài viết hay và chất lượng hơn trong tương lai nhé. À mà nếu bạn có bất kỳ câu hỏi nào thì đừng ngại comment hoặc liên hệ mình qua: Zalo - 0374226770 hoặc Facebook. Mình xin cảm ơn.

Momo: NGUYỄN ANH TUẤN - 0374226770

TPBank: NGUYỄN ANH TUẤN - 0374226770 (hoặc 01681423001)

image.png


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.