Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / ES6

Functional Programming – You Probably Already Know It!

4.16/5 (19 votes)
1 Jul 2020CPOL4 min read 14.6K  
Common concepts in functional programming language
In this post, we’ll explore common concepts in a functional programming language in TypeScript.

Functional programming as a concept has been around for years. But in the last 5-6 years, somehow, it has become the popular kid on the block.

Most of the folks I’ve talked with in recent years have been intimidated by functional programming, and specifically the popular languages, like, Scala, Erlang, Haskel, etc.

Well, my (official) introduction to functional programming was with Scala, and since I was used to verbose OO languages like C#, Java or JavaScript, Scala’s programming style required some time to get used to.
But, one thing that didn’t take getting used to, was functional programming.

What I didn’t realize was, that, since I had been coding in JavaScript/TypeScript for a few years now, I already knew a lot about typical functional programming concepts (like functions being first-class citizens, high-order functions, closures, etc).

Considering the statistic on stackoverflow, if you are developer, then there’s a 67.8% probability you already know functional programming. JavaScript has been the most popular programming language for the seventh year in a row now.

When it comes to top functional programming languages, JavaScript/TypeScript would, at best, get an honorable mention. But believe me, with the introduction of ES6 and evolution of TypeScript, it already supports a lot of functional programming concepts.
And if you’ve been coding in JavaScript/TypeScript for a few years now, you are (almost) a functional programming wizard.

So, what are some of the common concepts in a functional programming language:

  1. Functions as first-class citizens and higher-order functions
  2. Pure Functions
  3. Closures
  4. Currying
  5. (Tail) Recursion
  6. Immutability

We’ll try to explore at all these concepts in TypeScript in the following sections.

Functions as First-Class Citizens and Higher-Order Functions

First-class citizens: When we say functions are first-class citizens, it means, you can assign function to variables, pass them as arguments and have them as return types.

JavaScript
let funcAdd = (a: number, b: number): number => {
    return a + b;
}
console.log(funcAdd(5, 4)); // 9

High-order functions: A function is called high-order, when it can accept one or more functions as parameters or if it’s return type is function.

JavaScript
type paramFunc = (k: string) => number;
function higherFunc(fn: paramFunc, arr: string[]): number[] {
    let returnArray: number[] = []
    arr.forEach(elem => {
        returnArray.push(fn(elem));
    });
    return returnArray;
}

let lengthFunc = function (param: string): number {
    return param.length;
}

console.log(higherFunc(lengthFunc, ["hello", "bye"])); // Array(2) [5, 3]

Pure Functions

  • Pure Functions are methods which do not alter any external state, for example, any class level variable, external DB, file, etc.
  • Also, any external factors (like, time of the day) shouldn’t influence their behavior.
  • A pure function should always return the same output for the same set of inputs.
JavaScript
// Impure Function

var impureFunc = (): number => {
    return new Date().getMonth();
}
// Will return the numeric month depending on when you run it
console.log(impureFunc()); 

// Pure Function
var funcAdd = (a: number, b: number): number => {
    return a + b;
}
// Will always return output consistent with input
console.log(funcAdd(5, 4)); 

Closures

Closures are one of the cooler concepts in programming. Although they could lead to difficult to debug issues, but once you get the hang of it, you’ll love them.

Closures basically store the state of outside data in the function. If, at the time the function is compiled, it has a reference to a global (or parent function level) variable, it would continue to hold its value even when the parent function containing the variable goes out of scope (or has closed).

JavaScript
var timerFunc = (name: string) => {
    let functionVar = `Hello ${name}`;
    setTimeout(() => {
        console.log(`after 1000 ms - ${functionVar}`);
    }, 1000);
    console.log("exiting timerFunc");
}

timerFunc("chinmoy");

// exiting timerFunc
// after 1000 ms - Hello chinmoy

As you can see from the above example, although the timeFunc exited, the functionVar parameter’s value was still retained inside the function closure (cool right). Hence, when the timeout elapsed, they could still print the value of functionVar.

Currying

Currying is a very popular functional programming concept. Every language has a different flavor for currying (pun not intended), but TypeScript probably has the most bland flavor (pun intended ;)) of all.

It is basically a technique to construct functions that allows for partial application of the function’s arguments. What it basically means is, you can either call a curried function with all its parameters, or you could call the function with limited parameters and it would return a function which accepts the rest of the parameters.

JavaScript
function curryAdd(a: number): (b: number) => number {
    return (b: number): number => a + b;
}

// Partial application of the function
let moreAdd10 = curryAdd(10);

console.log(moreAdd10(3)); // 13
console.log(curryAdd(3)(10)); // 13

(Tail) Recursion

It’s a very popular opinion that functional programming favors recursion v/s looping. Well, although recursion is fancy and results is concise code, it can incur performance losses, since the results of every recursion are stored on a stack.

Hence, most functional programming languages favor something known as Tail Recursion.

Let’s looks at the most trivial sample code for recursion. Getting factorial of a number:

JavaScript
// Snippet for Normal Recursion
let factFunc = (param: number): number =>
    param== 0 ? 1 : param* factFunc (param- 1);

console.log(factFunc (10)); // 3628800


// Snippet for Tail Recursion
const factFuncTail = (param: number): number => fact(1, param);

const fact = (accum: number, val: number): number =>
    val == 1 ? accum : fact(accum * val, val - 1);

console.log(factFuncTail(10)); // 3628800

I found the tail recursion function a little difficult to understand but for engines optimized for tail recursion, it performs on par with looping.

Unfortunately, not all JavaScript engines are optimized for tail recursion, so you might incur performance penalties when writing recursive code in JavaScript/TypeScript.

Immutability

It’s one of the core best pratices when writing functional code. One should always strive to use immutable data types. Having these results in more robust and reliable code which is easier to debug.

Unfortunately, TypeScript doesn’t provide strictly immutable data structure. We have a notion of Immutability when using “const”, but these are not strictly immutable.

Let me show you what I’m talking about:

JavaScript
const cStr = "Hello, I am Immutable";
cstr = "Trying to mutate here"; // This will produce an error

const lst = ["A", "B", "C", "D"];
list = ["X", "Y"];              // This will produce an error

list.push("X"); // This will compile and run just fine (Not immutable after all, huh!)

There are a few other characteristics of functional programming languages, but the core idea which I’m trying to convey is that if you have experience coding in JavaScript/TypeScript, you already know functional programming.

Of course, this doesn’t mean there wouldn’t be a learning curve when learning a language like Scala, but, I believe it’ll not be very steep.

Happy coding!!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)