Chapter 2. Fundamentals of Functional Programming

By now, you've seen a small glimpse of what functional programming can do. But what exactly is functional programming? What makes one language functional and not another? What makes one programming style functional and not another?

In this chapter, we will first answer these questions and then cover the core concepts of functional programming:

  • Using functions and arrays for control flow
  • Writing pure functions, anonymous functions, recursive functions, and more
  • Passing functions around like objects
  • Utilizing the map(), filter(), and reduce() functions

Functional programming languages

Functional programming languages are languages that facilitate the functional programming paradigm. At the risk of oversimplifying, we could say that, if a language includes the features required for functional programming, then it is a functional language—as simple as that. In most cases, it's the programming style that truly determines whether a program is functional or not.

What makes a language functional?

Functional programming cannot be performed in C. Functional programming cannot be performed in Java (without a lot of cumbersome workarounds for "almost" functional programming). Those and many more languages simply don't contain the constructs to support it. They are purely object-oriented and strictly non-functional languages.

At the same time, object-oriented programming cannot be performed on purely functional languages, such as Scheme, Haskell, and Lisp, just to name a few.

However, there are certain languages that support both models. Python is a famous example, but there are others: Ruby, Julia, and—here's the one we're interested in—JavaScript. How can these languages support two design patterns that are very different from each other? They contain the features required for both programming paradigms. However, in the case of JavaScript, the functional features are somewhat hidden.

But really, it's a little more involved than that. So what makes a language functional?

Characteristic

Imperative

Functional

Programming Style

Perform step-by-step tasks and manage changes in state

Define what the problem is and what data transformations are needed to achieve the solution

State Changes

Important

Non-existent

Order of Execution

Important

Not as important

Primary Flow Control

Loops, conditionals, and function calls

Function calls and recursion

Primary Manipulation Unit

Structures and class objects

Functions as first-class objects and data sets

The syntax of the language must allow for certain design patterns, such as an inferred type system, and the ability to use anonymous functions. Essentially, the language must implement Lambda calculus. Also, the interpreter's evaluation strategy should be non-strict and call-by-need (also known as deferred execution), which allows for immutable data structures and non-strict, lazy evaluation.

Advantages

You could say that the profound enlightenment you experience when you finally "get it" will make learning functional programming worth it. An experience such as this will make you a better programmer for the rest of your life, whether you actually become a full-time functional programmer or not.

But we're not talking about learning to meditate; we're talking about learning an extremely useful tool that will make you a better programmer.

Formally speaking, what exactly are the practical advantages of using functional programming?

Cleaner code

Functional programs are cleaner, simpler, and smaller. This simplifies debugging, testing, and maintenance.

For example, let's say we need a function that converts a two-dimensional array into a one-dimensional array. Using only imperative techniques, we could write it the following way:

function merge2dArrayIntoOne(arrays) {
  var count = arrays.length;
  var merged = new Array(count); 
  var c = 0;
  for (var i = 0; i < count; ++i) {
    for (var j = 0, jlen = arrays[i].length; j < jlen; ++j) {
      merged[c++] = arrays[i][j];
    }
  }
  return merged
}

And using functional techniques, it could be written as follows:

varmerge2dArrayIntoOne2 = function(arrays) {
  return arrays.reduce( function(p,n){
    return p.concat(n);
  });
};

Both of these functions take the same input and return the same output. However, the functional example is much more concise and clean.

Modularity

Functional programming forces large problems to be broken down into smaller instances of the same problem to be solved. This means that the code is more modular. Programs that are modular are clearly specified, easier to debug, and simpler to maintain. Testing is easier because each piece of modular code can potentially be checked for correctness.

Reusability

Functional programs share a variety of common helper functions, due to the modularity of functional programming. You'll find that many of these functions can be reused for a variety of different applications.

Many of the most common functions will be covered later in this chapter. However, as you work as a functional programmer, you will inevitably compile your own library of little functions that can be used over and over again. For example, a well-designed function that searches through the lines of a configuration file could also be used to search through a hash table.

Reduced coupling

Coupling is the amount of dependency between modules in a program. Because the functional programmer works to write first-class, higher-order, pure functions that are completely independent of each other with no side effects on global variables, coupling is greatly reduced. Certainly, functions will unavoidably rely on each other. But modifying one function will not change another, so long as the one-to-one mapping of inputs to outputs remains correct.

Mathematically correct

This last one is on a more theoretical level. Thanks to its roots in Lambda calculus, functional programs can be mathematically proven to be correct. This is a big advantage for researchers who need to prove the growth rate, time complexity, and mathematical correctness of a program.

Let's look at Fibonacci's sequence. Although it's rarely used for anything other than a proof-of-concept, it illustrates this concept quite well. The standard way of evaluating a Fibonacci sequence is to create a recursive function that expresses fibonnaci(n) = fibonnaci(n-2) + fibonnaci(n–1) with a base case to return 1 when n < 2, which makes it possible to stop the recursion and begin adding up the values returned at each step in the recursive call stack.

This describes the intermediary steps involved in calculating the sequence.

var fibonacci = function(n) {
  if (n < 2) {
    return 1;
  }
  else {
    return fibonacci(n - 2) + fibonacci(n - 1);
  }
}
console.log( fibonacci(8) );
// Output: 34

However, with the help of a library that implements a lazy execution strategy, an indefinite sequence can be generated that states the mathematical equation that defines the entire sequence of numbers. Only as many numbers as needed will be computed.

var fibonacci2 = Lazy.generate(function() {
  var x = 1,
  y = 1;
  return function() {
    var prev = x;
    x = y;
    y += prev;
    return prev;
  };
}());

console.log(fibonacci2.length());// Output: undefined

console.log(fibonacci2.take(12).toArray());// Output: [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144] 

var fibonacci3 = Lazy.generate(function() {
  var x = 1,
  y = 1;
  return function() {
    var prev = x;
    x = y;
    y += prev;
    return prev;
  };
}());

console.log(fibonacci3.take(9).reverse().first(1).toArray());// Output: [34]

The second example is clearly more mathematically sound. It relies on the Lazy.js library of JavaScript. There are other libraries that can help here as well, such as Sloth.js and wu.js. These will be covered in Chapter 3, Setting Up the Functional Programming Environment.

Functional programming in a nonfunctional world

Can functional and nonfunctional programming be mixed together? Although this is the subject of Chapter 7, Functional & Object-oriented Programming in JavaScript, it is important to get a few things straight before we go any further.

This book is not intended to teach you how to implement an entire application that strictly adheres to the rigors of pure functional programming. Such applications are rarely appropriate outside Academia. Rather, this book will teach you how to use functional programming design strategies within your applications to complement the necessary imperative code.

For example, if you need the first four words that only contain letters out of some text, they could naively be written like this:

var words = [], count = 0;
text = myString.split(' ');
for (i=0; count<4, i<text.length; i++) {
  if (!text[i].match(/[0-9]/)) {
    words = words.concat(text[i]);
    count++;
  }
}
console.log(words);

In contrast, a functional programmer might write them as follows:

var words = [];
var words = myString.split(' ').filter(function(x){
  return (! x.match(/[1-9]+/));
}).slice(0,4);
console.log(words);

Or, with a library of functional programming utilities, they can be simplified even further:

var words = toSequence(myString).match(/[a-zA-Z]+/).first(4);

The key to identifying functions that can be written in a more functional way is to look for loops and temporary variables, such as words and count instances in the preceding example. We can usually do away with both temporary variables and loops by replacing them with higher-order functions, which we will explore later in this chapter.

Is JavaScript a functional programming language?

There is one last question we must ask ourselves. Is JavaScript a functional language or a non-functional language?

JavaScript is arguably the world's most popular and least understood functional programming language. JavaScript is a functional programming language in C-like clothing. Its syntax is undeniably C-like, meaning it uses C's block syntax and in-fix ordering. And it's one of the worst named languages in existence. It doesn't take a lot of imagination to see how so many people can confuse JavaScript as being related to Java; somehow, its name implies that it should be! But in reality it has very little in common with Java. And, to really cement the idea that JavaScript is an object-oriented language, libraries and frameworks such as Dojo and ease.js have been hard at work attempting to abstract it and make it suitable for object-oriented programming. JavaScript came of age in the 1990s when OOP was all the buzz, and we've been told that JavaScript is object-oriented because we want it to be so badly. But it is not.

Its true identity is much more aligned with its ancestors: Scheme and Lisp, two classic functional languages. JavaScript is a functional language, all the way. Its functions are first-class and can be nested, it has closures and compositions, and it allows for currying and monads. All of these are key to functional programming. Here are a few more reasons why JavaScript is a functional language:

  • JavaScript's lexical grammar includes the ability to pass functions as arguments, has an inferred type system, and allows for anonymous functions, higher-order functions, closures and more. These facts are paramount to achieving the structure and behavior of functional programming.
  • It is not a pure object-oriented language, with most object-oriented design patterns achieved by copying the Prototype object, a weak model for object-oriented programming. European Computer Manufacturers Association Script (ECMAScript), JavaScript's formal and standardized specifications for implementation, states the following in specification 4.2.1:

    "ECMAScript does not contain proper classes such as those in C++, Smalltalk, or Java, but rather, supports constructors which create objects. In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behavior. In ECMAScript, the state and methods are carried by objects, and structure, behavior and state are all inherited."

  • It is an interpreted language. Sometimes called "engines", JavaScript interpreters often closely resemble Scheme interpreters. Both are dynamic, both have flexible datatypes that easily combine and transform, both evaluate the code into blocks of expressions, and both treat functions similarly.

That being said, it is true that JavaScript is not a pure functional language. What's lacking is lazy evaluation and built-in immutable data. This is because most interpreters are call-by-name and not call-by-need. JavaScript also isn't very good with recursion due to the way it handles tail calls. However, all of these issues can be mitigated with a little bit of attention. Non-strict evaluation, required for infinite sequences and lazy evaluation, can be achieved with a library called Lazy.js. Immutable data can be achieved simply by programming technique, but this requires more programmer discipline rather than relying on the language to take care of it. And recursive tail call elimination can be achieved with a method called Trampolining. These issues will be addressed in Chapter 6, Advanced Topics & Pitfalls in JavaScript.

Many debates have been waged over whether or not JavaScript is a functional language, an object-oriented language, both, or neither. And this won't be the last debate.

In the end, functional programming is way of writing cleaner code through clever ways of mutating, combining, and using functions. And JavaScript provides an excellent medium for this approach. If you really want to use JavaScript to its full potential, you must learn how to use it as a functional language.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset