DEV Community

loading...
Cover image for Fundamentals of Functional JavaScript

Fundamentals of Functional JavaScript

anmshpndy profile image Animesh Pandey Updated on ・17 min read

Introduction

Is Functional Programming any style of code that utilizes functions? If only it was that simple!
Functions are indeed at the core of Functional Programming, but it's how we use those functions that make our implementation functional.

This article aims to lay down some fundamentals of Functional Programming, while mainly pertaining to their application in JavaScript, that will help you understand :

  1. What are Functions?
  2. Functions versus Procedures
  3. Declarative versus Imperative Programming
  4. Understanding Function Inputs and Outputs

These foundations will immensely aid you in grasping further concepts of Functional JavaScript, to be covered in future articles, if this one helps.

The next article will cover :

  1. Function Purity (Pure versus Impure Functions)
  2. Side Effects
  3. Extracting and Containing Impurity
  4. How all of this collectively defines what Functional Programming is, and why it is used
  5. Is JavaScript a Functional Programming Language?
  6. Why consider Functional Programming style for your code?

Stay tuned!


1. What are Functions?

Well, like any introductory programming class will tell you, a function is a reusable piece of code that performs some task upon execution. While this definition is reasonable, it misses out an important perspective that is the core of a function as it applies to Functional Programming.

Let's try to understand Functions more completely, with the example of very basic Mathematics.

You may remember reading about f(x) in school, or the equation y = f(x).
Let's assume the equation f(x) = x2 - 1. What does that mean? What does it mean to graph that equation? Here's the graph:
Plot for `f(x) = x^2 - 1`

It's equivalent to :

function f(x) {
   return Math.pow(x,2) - 1;
}
Enter fullscreen mode Exit fullscreen mode

What you can notice is that for any value of x, say 1, if you plug it into the equation, you get 0. What is 0, though? It's the return value of the f(x) function, which we earlier said represents a y value.

In math, a function always takes input(s), and always gives an output. A term you'll often hear around FP is "morphism"; this is a fancy way of describing a set of values that maps to another set of values, like the inputs of a function related to the outputs of that function.

In our code, however, we can define functions with all sorts of input(s) and output(s), even though they'll rarely be interpreted as a visually plotted curve on a graph.

As such, a more complete definition of function would be :

A function is the semantic relationship between input and computed output.

Also note the usage of function and function throughout this article. While function is the concept we're discussing, function is just the JavaScript keyword.

Essentially, Functional Programming is about embracing using functions as functions in this mathematical sense.


2. Functions vs Procedures?

The terms Functions and Procedures are often used interchangeably, but they actually mean different things.

A Procedure is an arbitrary collection of functionality. It may have inputs, it may not. It may have an output (as a return value), it may not.

Whereas, a Function takes input(s) and definitely always has a return value.

For Functional Programming, we use functions as much as possible, and trying to avoid procedures wherever possible. All your functions should take input(s) and return output(s).

Based upon this knowledge, let's consider the following example :

// Example 1: Function or Procedure?

function addPokémon(team1 = 0, team2 = 0, team3 = 0) {
    var total = team1 + team2 + team3;
    console.log(total);
}

function countPokémon(currentTeam = 6, ...args) {
    return addPokémon(currentTeam, ...args);
}

countPokémon();
// Output : 6

countPokémon(6, 5, 6);
// Output : 17
Enter fullscreen mode Exit fullscreen mode

Try to assess whether function addPokémon and countPokémon are functions or procedures?

Here are some basic observations :

  1. addPokémon has a defined input, but no output specified by return. It should be a procedure.
  2. countPokémon has a defined input and a defined return, so it should be a function?

We're correct about addPokémon being a procedure, but countPokémon is also a procedure, and not a function, since it makes a call to a procedure within itself.

In summary :

Note that a function that calls a procedure within itself, is also a procedure. The "impurity" of a procedure, which is a concept to be explained further down, trickles down and "pollutes" all those who directly or indirectly call it.


Now, we would probably like to understand how to convert the last example's procedures into functions?

Based on the more complete definition of a function mentioned in the last section, try to make changes to the last example, before looking ahead for one of many possible solutions. For this example, it should be pretty straight-forward.

// Example 2: Converting Procedures to Functions?

function addPokémon(team1 = 0, team2 = 0, team3 = 0) {
    var total = team1 + team2 + team3;
    return total;
    // Instead of logging a value, we returned it,
    // so there's a proper output/return now.
}

function countPokémon(currentTeam = 6, ...args) {
    return addPokémon(currentTeam, ...args);
    // Now, a call to a function, not a procedure, is returned
}

console.log(countPokémon());
// Output : 6

console.log(countPokémon(6, 5, 6));
// Output : 17
Enter fullscreen mode Exit fullscreen mode

Let's look at one more example for differentiating procedures and functions.

// Example 3. Identifying functions and procedures

function neighbouringPokémonID(x) {
    x = Number(x);
    return [x - 1, x + 1];
}

function generateNeighboursForTeam(team) {
    var teamIDs = Object.keys(team);
    teamIDs.forEach(element => 
        console.log(neighbouringPokémonID(element)));
}

var myTeam = {
    25: "Pikachu",
    155: "Cyndaquil"
};

generateNeighboursForTeam(myTeam);
// Output :
// [24, 26]
// [154, 156]
Enter fullscreen mode Exit fullscreen mode

This snippet effectively returns the Pokédex IDs of the immediate neighbours of a Pokémon, given its own ID.

Clearly, neighbouringPokémonID is a function, as it has an input and returns an output based upon it.

Also, generateNeighboursForTeam is a procedure, as it doesn't return anything.

Once again, we can modify this example such that both are functions.

// Example 4. Converting the procedure to a function

function neighbouringPokémonID(x) {
    x = Number(x);
    return [x - 1, x + 1];
}

function generateNeighboursForTeam(team) {
    var teamIDs = Object.keys(team);
    var neighbourIDs = [];
    // Use a temporary array to store computation
    teamIDs.forEach(element =>
        neighbourIDs.push(neighbouringPokémonID(element)));
    return neighbourIDs;
}

var myTeam = {
    25: "Pikachu",
    155: "Cyndaquil"
};

generateNeighboursForTeam(myTeam);
// Output :
// [[24, 26],[154, 156]]
Enter fullscreen mode Exit fullscreen mode

3. Declarative vs Imperative Programming?

Another basic concept to be familiar with is, the distinction between Declarative and Imperative styles of coding, which is honestly a bit relative in its meaning.

There's no style that's absolutely Declarative or absolutely Imperative. It's a spectrum in itself.

That said, let's introduce ourselves to a common, simple definition.

Imperative programming is like how you do something, and Declarative programming is more like what you do.”

It's a bit ambiguous and open-ended, so let's take a small example.

Suppose, you are trying to help your little brother with the basics of the latest Pokémon game. Specifically, about catching wild Pokémon.

Declarative: Throw a pokéball when the pokémon is weak. (What to do)

Imperative: When the health bar of the pokémon is below 30%, press X to throw a pokéball. (Exactly how to do it)

In general, explicitly listing out all the steps one by one, is Imperative. It's rather robotic to understand, and requires going through it line by line.

And utilizing some level of abstraction and trusted helper functions, to list the steps in a manner in which it only presents the basic idea, is Declarative. It's easier to understand, as we don't need to bother about how something is happening, rather what is happening.

As what and how can be rather subjective, we can't draw a hard boundary around what is declarative or imperative.

For example, for a person programming in machine language, which is super imperative, Java can seem rather declarative. Or for a person who works on a purely functional language, like Haskell or Clojure, even functional implementations in JavaScript can feel rather imperative.

Our concern at the moment, which is to set the foundations of Functional Programming and Functional JavaScript, we need to understand that we should make our code as declarative as possible, by utilizing functions.

Moving on, let's understand a bit more about Function Inputs and Outputs.


4. Function Inputs

This section covers some more aspects of Function Inputs, chiefly :

  • Arguments and Parameters
  • Defaulting Parameters
  • Counting Inputs
  • Arrays of Arguments
  • Parameter Destructuring
  • Benefits of Declarative Style
  • Named Arguments
  • Unordered Parameters

Let's get started.

a. Arguments and Parameters

There's often a slight confusion about the difference between arguments and parameters.

Put simply, arguments are the values you pass into a function, and parameters are the named variables inside the function that receive those values.

Note: In JavaScript, the number of arguments do not need to match the number of parameters. If you pass more arguments than you have declared parameters to receive them, the values pass in just fine.

These extra arguments can be accessed in a few ways, including the args object. If you pass fewer arguments, each unmatched parameter is assigned undefined.


b. Defaulting Parameters

Parameters can declare default values. In the case where the argument for that parameter is not passed, or it's passed the value undefined, the default assignment expression is substituted.

function f(x = 10) {
    console.log(x);
}

f();                // Output : 10
f(undefined);       // Output : 10
f(null);            // Output : null
f(0);               // Output : 0
Enter fullscreen mode Exit fullscreen mode

It's always a good practice to think about any default cases that can aid the usability of your functions.


c. Arity, or Count of Inputs

The number of arguments a function "expects" is determined by the number of parameters that are declared.

function f(x,y,z,w) {
    // something
}

f.length;
// Output :
// 4
Enter fullscreen mode Exit fullscreen mode

f(..) expects 4 arguments, as it has 4 declared parameters. This count has a special term: Arity, which is the number of parameters in a function declaration. The arity of f(..) is 4.

Furthermore, a function with arity 1 is additionally called unary, a function with arity 2 is also called binary, and a function with arity 3 or higher is named n-ary.

The length property of that function reference returns its arity.

While this may sound simple, the implications are far-reaching.

One reason for determining the arity during execution would be if a piece of code received a function reference from multiple sources, and has to send different values depending on the arity of each.

For example, let's say a fn function reference could expect one, two, or three arguments, but you always want to just pass a variable x in the last position:

// `fn` is set to some function reference
// `x` exists with some value

if (fn.length == 1) {
    fn(x);
}
else if (fn.length == 2) {
    fn(undefined, x);
}
else if (fn.length == 3) {
    fn(undefined, undefined, x);
}
Enter fullscreen mode Exit fullscreen mode

Tip: The length property of a function is read-only and it's determined at the time you declare the function. It should be thought of as essentially a piece of metadata that describes something about the intended usage of the function.

One gotcha to be aware of is that certain kinds of parameter list variations can make the length property of the function report something different than you might expect:

function foo(x,y = 2) {
    // something
}

function bar(x,...args) {
    // something
}

function baz( {a,b} ) {
    // something
}

foo.length;             // Output : 1
bar.length;             // Output : 1
baz.length;             // Output : 1
Enter fullscreen mode Exit fullscreen mode

What about counting the number of arguments the current function call received? This was once trivial, but now the situation is slightly more complicated. Each function has an arguments object (array-like) available that holds a reference to each of the arguments passed in. You can then inspect the length property of arguments to figure out how many were actually passed:

function f(x,y,z) {
    console.log(arguments.length);
}

f(3, 4);   
// Output : 
// 2
Enter fullscreen mode Exit fullscreen mode

As of ES5 (and strict mode, specifically), arguments is considered by some to be sort of deprecated; many avoid using it if possible. However, arguments.length, and only that, is okay to keep using for those cases where you need to care about the passed number of arguments.

A function signature that accepts an indeterminate amount of arguments is referred to as a variadic function.

Say you do need to access the arguments in a positional array-like way, possibly because you're accessing an argument that doesn't have a formal parameter at that position. How do we do it?

ES6 to the rescue! Let's declare our function with the ... operator, referred to as "spread", "rest", or "gather":

function f(x,y,z,...args) {
    // something
}
Enter fullscreen mode Exit fullscreen mode

The ...args in the parameter list is an ES6 declarative form that tells the engine to collect all remaining arguments (if any) not assigned to named parameters, and put them in a real array named args. args will always be an array, even if it's empty. But it will not include values that are assigned to the x, y, and z parameters, only anything else that's passed in beyond those first three values.

function f(x,y,z,...args) {
    console.log(x, y, z, args);
}

f();                // undefined undefined undefined []
f(1, 2, 3);         // 1 2 3 []
f(1, 2, 3, 4);      // 1 2 3 [ 4 ]
f(1, 2, 3, 4, 5);   // 1 2 3 [ 4, 5 ]
Enter fullscreen mode Exit fullscreen mode

So, if you want to design a function that can account for an arbitrary number of arguments, use ...args.

You can use the ... operator in the parameter list even if there are no other formal parameters declared.

function (...args) {
    // something
}
Enter fullscreen mode Exit fullscreen mode

args will now be the complete array of arguments, whatever they are, and you can use args.length to know exactly how many arguments have been passed in.


d. Arrays of Arguments

What if you wanted to pass along an array of values as the arguments to a function call?

function f(...args) {
    console.log(args[3]);
}

var arr = [1, 2, 3, 4, 5];
f(...arr);  
// Output :                    
// 4
Enter fullscreen mode Exit fullscreen mode

Our new friend, the ... operator is used here, but now not just in the parameter list; it's also used in the argument list at the call-site.

It has the opposite behavior in this context.
In a parameter list, we said it gathered arguments together. In an argument list, it spreads them out. So the contents of arr are actually spread out as individual arguments to the f(..) call.

Also, multiple values and ... spreadings can be interweaved, as needed:

var arr = [2];

f(1, ...arr, 3, ...[4,5]);  
// Output :   
// 4
Enter fullscreen mode Exit fullscreen mode

Think of ... in this symmetric sense: in a value-list position, it spreads. In an assignment position -- like a parameter list, because arguments get assigned to parameters -- it gathers.

... makes working with arrays of arguments much easier.


e. Parameter Destructuring

Consider the variadic f(..) from the previous section:

function f(...args) {
    // something
}

f( ...[1,2,3]);
Enter fullscreen mode Exit fullscreen mode

What if we wanted to change that interaction so the caller of our function passes in an array of values instead of individual argument values? Just drop the two ... usages:

function f(args) {
    // something
}

f([1,2,3]);
Enter fullscreen mode Exit fullscreen mode

Simple enough. But what if now we wanted to give a parameter name to each of the first two values in the passed-in array? We aren't declaring individual parameters anymore, so it seems we lost that ability.

Thankfully, ES6 destructuring is the answer. Destructuring is a way to declare a pattern for the kind of structure (object, array, etc.) that you expect to see, and how decomposition (assignment) of its individual parts should be processed.

Consider:

function f([x,y,...args] = []) {
    // something
}

f([1,2,3]);
Enter fullscreen mode Exit fullscreen mode

Do you spot the [ .. ] brackets around the parameter list now? This is called array parameter destructuring.

In this example, destructuring tells the engine that an array is expected in this assignment position (aka parameter). The pattern says to take the first value of that array and assign it to a local parameter variable called x, the second to y, and whatever is left is gathered into args.


f. Benefits of Declarative Style

Considering the destructured f(..) we just looked at, we could instead have processed the parameters manually:

function f(params) {
    var x = params[0];
    var y = params[1];
    var args = params.slice(2);
    // something
}
Enter fullscreen mode Exit fullscreen mode

But here we highlight a principle that declarative code communicates more effectively than imperative code.

Declarative code (for example, the destructuring in the former f(..) snippet, or the ... operator usages) focuses on what the outcome of a piece of code should be.

Imperative code (such as the manual assignments in the latter snippet) focuses more on how to get the outcome. The outcome is coded there, but it's not as clear because it's crowded by the details of how we got there.

The earlier f(..) is regarded as more readable, because the destructuring hides the unnecessary details of how to manage the parameter inputs.

Wherever possible, we should strive for declarative, self-explanatory code.


g. Named Arguments

Just as we can destructure array parameters, we can destructure object parameters:

function f({x,y} = {}) {
    console.log(x, y);
}

f({
    y: 3
});
// Output :                                      
// undefined 3
Enter fullscreen mode Exit fullscreen mode

We pass in an object as the single argument, and it's destructured into two separate parameter variables x and y, which are assigned the values of those corresponding property names from the object passed in. It didn't matter that the x property wasn't on the object; it just ended up as a variable with undefined like you'd expect.

With a normal call-site like f(undefined,3), position is used to map from argument to parameter; we put the 3 in the second position to get it assigned to a y parameter.

But at this call-site where parameter destructuring is involved, a simple object-property indicates which parameter (y) the argument value 3 should be assigned to.

Some languages have an explicit feature for this: named arguments. In other words, at the call-site, labeling an input value to indicate which parameter it maps to. JavaScript doesn't have named arguments, but parameter object destructuring is the next best thing.


h. Unordered Parameters

Another key benefit is that named arguments, by virtue of being specified as object properties, are not fundamentally ordered. That means we can specify inputs in whatever order we want:

function f({x,y} = {}) {
    console.log(x, y);
}

f({
    y: 3
});  
// Output :                  
// undefined 3
Enter fullscreen mode Exit fullscreen mode

The call-site is no longer cluttered by ordered-placeholders like undefined to skip a parameter.


Function Outputs

This section covers some more aspects of Function Outputs.

In JavaScript, functions always return a value. These three functions all have identical return behavior:

function foo() {}

function bar() {
    return;
}

function baz() {
    return undefined;
}
Enter fullscreen mode Exit fullscreen mode

The undefined value is implicitly returned if you have no return or if you just have an empty return;.

But keeping as much with the spirit of Functional Programming function definition as possible -- using functions and not procedures -- our functions should always have outputs, which means they should explicitly return a value, and usually not undefined.

A return statement can only return a single value. So if your function needs to return multiple values, your only viable option is to collect them into a compound value like an array or an object:

function f() {
    var retValue1 = 1;
    var retValue2 = 3;
    return [retValue1, retValue2];
}
Enter fullscreen mode Exit fullscreen mode

Then, we'll assign x and y from two respective items in the array that comes back from f():

var [x, y] = f();
console.log(x + y);
// Output : 4
Enter fullscreen mode Exit fullscreen mode

Collecting multiple values into an array (or object) to return, and subsequently destructuring those values back into distinct assignments, is a way to transparently express multiple outputs for a function.

Let's cover some concepts related to Function Outputs, chiefly :

  • Early Returns
  • Unreturned Outputs
  • Higher-Order Functions (HOFs or Functions of Functions)

a. Early Returns

The return statement doesn't just return a value from a function. It's also a flow control structure; it ends the execution of the function at that point.

A function with multiple return statements thus has multiple possible exit points, meaning that it may be harder to read a function to understand its output behavior if there are many paths that could produce that output.

Consider:

function f(x) {
    if (x > 10) return x + 1;

    var y = x / 2;

    if (y > 3) {
        if (x % 2 == 0) return x;
    }

    if (y > 1) return y;

    return x;
}

f(2);    // Output : 2
f(4);    // Output : 2
f(8);    // Output : 8
f(12);   // Output : 13
Enter fullscreen mode Exit fullscreen mode

First off, f(x) is highly unreadable and difficult to follow. Dry-running this in your mind is quite tedious. That's because we're using return not just to return different values, but also as a flow control construct to quit a function's execution early in certain cases.

Consider this version of the code:

function f(x) {
    var retValue;

    if (retValue == undefined && x > 10) {
        retValue = x + 1;
    }

    var y = x / 2;

    if (y > 3) {
        if (retValue == undefined && x % 2 == 0) {
            retValue = x;
        }
    }

    if (retValue == undefined && y > 1) {
        retValue = y;
    }

    if (retValue == undefined) {
        retValue = x;
    }

    return retValue;
}
Enter fullscreen mode Exit fullscreen mode

This version is unquestionably more verbose. But it is slightly simpler logic to follow, because every branch where retValue can get set is guarded by the condition that checks if it's already been set.

Rather than returning from the function early, we used normal flow control (if logic) to determine the retValue's assignment. At the end, we simply return retValue.

In summary, it's more readable to have just a single return at the end. Try to figure out the most explicit way to express the logic.


b. Unreturned Outputs

One technique that you've probably used in most code you've written, and maybe didn't even think about it much, is to have a function output some or all of its values by simply changing variables outside itself.

Remember our f(x) = x2 - 1 function from earlier? We could have defined it like this in JS:

var y;
function f(x) {
    y = (2 * Math.pow( x, 2 )) + 3;
}
Enter fullscreen mode Exit fullscreen mode

We could just as easily have returnd the value instead of setting it into y from within the function:

function f(x) {
    return (2 * Math.pow( x, 2 )) + 3;
}
Enter fullscreen mode Exit fullscreen mode

Both functions accomplish the same task, so is there any reason we should pick one version over the other?

One way to explain the difference is that the return in the latter version signals an explicit output, whereas the y assignment in the former is an implicit output.

But changing a variable in an outer scope, as we did with the y assignment inside of f(..), is just one way of achieving an implicit output. A more subtle example is making changes to non-local values via reference.

Consider:

function sum(list) {
    var total = 0;
    for (let i = 0; i < list.length; i++) {
        if (!list[i]) list[i] = 0;
        total = total + list[i];
    }
    return total;
}

var nums = [ 1, 3, 9, 27, , 84 ];
sum(nums);
// Output : 
// 124
Enter fullscreen mode Exit fullscreen mode

The most obvious output from this function is the sum 124, which we explicitly returned. But instead of an undefined empty slot value in position 4, now there's a 0.

The harmless looking list[i] = 0 operation ended up affecting the array value on the outside, even though we operated on a local list parameter variable.

Why? Because list holds a reference-copy of the nums reference, not a value-copy of the [1,3,9,..] array value. JavaScript uses references and reference-copies for arrays, objects, and functions, so we may create an accidental output from our function all too easily.

This implicit function output has a special name in the FP world: Side Effects. And a function that has no side effects also has a special name: Pure Function. Both these concepts will be covered in the next article.


c. Higher-Order Functions (HOFs or Functions of Functions)

Functions can receive and return values of any type. A function that receives or returns one or more other function values has the special name: higher-order function.

Consider:

function forEach(list,fn) {
    for (let v of list) {
        fn( v );
    }
}

forEach( [1,2,3,4,5], function each(val){
    console.log( val );
} );

// Output :
// 1 2 3 4 5
Enter fullscreen mode Exit fullscreen mode

forEach(..) is a higher-order function because it receives a function as an argument.

A higher-order function can also output another function, like:

function f() {
    return function upper(x){
        return x.toUpperCase();
    };
}

var g = f();
g("Hello!");

// Output :
// HELLO!
Enter fullscreen mode Exit fullscreen mode

return is not the only way to "output" an inner function:

function f() {
    return g(function upper(x){
        return x.toUpperCase();
    } );
}

function g(func) {
    return func("Hello!");
}

f();

// Output :
// HELLO!
Enter fullscreen mode Exit fullscreen mode

Functions that treat other functions as values are higher-order functions by definition. These are very crucial to Functional Programming!


Summary

We covered the following concepts in this article :

  1. What are Functions?
  2. Functions versus Procedures
  3. Declarative versus Imperative Programming
  4. Function Inputs
    • Arguments and Parameters
    • Defaulting Parameters
    • Counting Inputs
    • Arrays of Arguments
    • Parameter Destructuring
    • Benefits of Declarative Style
    • Named Arguments
    • Unordered Parameters
  5. Function Outputs
    • Early Returns
    • Unreturned Outputs
    • Higher-Order Functions (HOFs or Functions of Functions)

The next article will cover :

  1. Function Purity (Pure versus Impure Functions)
  2. Side Effects
  3. Extracting and Containing Impurity
  4. How all of this collectively defines what Functional Programming is, and why it is used
  5. Is JavaScript a Functional Programming Language?
  6. Why consider Functional Programming style for your code?

Credits

  • Functional-Light JS book by Kyle Simpson, for inspiring this article, as well as,
  • FrontEnd Masters : Functional-Light JavaScript

Thanks a lot for reading! ❤️


Follow me?

Dev.to | Twitter | Hashnode | Medium | GitHub | LinkedIn | Buy Me A Coffee


Discussion (32)

pic
Editor guide
Collapse
bmehder profile image
Brad Mehder

I find FP to be intriguing, but I have been making apps with Svelte lately. I find it difficult to use FP patterns with a framework that is based on a MVVM pattern. How can I make use of FP with MVVM?

I am looking forward to the rest of the articles in this series. Thank you for doing this!

Collapse
anmshpndy profile image
Animesh Pandey Author

Agreed. FP patterns, in general, can sometimes be difficult to implement with many frameworks. In that case, we can only try our best to accommodate them. Perhaps, I'll try to cover this by taking examples of some common frameworks. I personally haven't worked with Svelte yet, I'll check it out sometime.

In my own view, a framework that doesn't allow enough freedom to try various design patterns, is too restrictive to work with; since it basically feeds you its own guidelines at that point, and forces you to only think along those lines.

This might be a good idea to cover later. Thanks for the suggestion!

And, thank you for enjoying this! It takes a lot of time to put these together, but it is very rewarding if it helps even one person out.

Collapse
myleftshoe profile image
myleftshoe

Have you seen cyclejs? Didn't/hasn't really taken off - possibly because it's too far from the norm. It's a "framework" that suffers the opposite restrictiveness problem to most others - forces you to think functionally

Thread Thread
anmshpndy profile image
Animesh Pandey Author

Ah, no hadn't heard of it yet. It looks really interesting from an overview of its docs. Time to try it out someday? Thanks for sharing!

Thread Thread
myleftshoe profile image
myleftshoe

Check out the authors videos about it too - really good

Thread Thread
anmshpndy profile image
Animesh Pandey Author

Are you talking about these ones on Egghead? app.egghead.io/playlists/cycle-js-...

Thread Thread
myleftshoe profile image
myleftshoe

Sorry, haven't got the link anymore but there's one where he describes the philosophy behind it

Thread Thread
myleftshoe profile image
Thread Thread
anmshpndy profile image
Animesh Pandey Author

Oh my. It is ultra insightful! Will have to go through it a couple of times to properly appreciate.

Thread Thread
johnkazer profile image
Thread Thread
anmshpndy profile image
Collapse
marwaneb profile image
Marwan El Boussarghini • Edited

Very cool article thanks for writing it. I'm also super enthusiastic about FP and it's good that people write content about it 😄

About the section "a. Early Returns" I tend to disagree with you: let's take this code

function getPokemonWeaknesses({ types = [] }) {
  let weaknesses = []

  if (types.includes('WATER')) {
    if (!types.includes('EARTH')) {
      weaknesses = ['ELECTRICITY']
    } else {
      weaknesses = ['PLANT']
    }
  }
  if (types.includes('FIRE')) {
    if (!types.includes('WATER')) {
      weaknesses = ['WATER']
    } else {
      weaknesses = ['PSY']
    }
  }
  return weaknesses
}
Enter fullscreen mode Exit fullscreen mode

I generally feel that I would have far more troubles debugging it and understanding it at first glance than a code like this one:

function getPokemonWeaknesses({ types = [] }) {
  if (types.includes('WATER')) {
    if (!types.includes('EARTH')) return ['ELECTRICITY']
    return ['PLANT']
  }

  if (types.includes('FIRE')) {
    if (types.includes('WATER')) return ['PSY']
    if (types.includes('PLANT')) return ['COMBAT']
    return ['PLANT']
  }

  return []
}
Enter fullscreen mode Exit fullscreen mode

Maybe it's more of a personal preference but I would be super interested to read how people feel about this 👍🏽

PS: sorry for my knowledge on Pokemon types, it has been a long time since I've played any Pokemon game 😅

Collapse
anmshpndy profile image
Animesh Pandey Author • Edited

Haha, thanks. 😊

In the end, I guess the point is to make the code more readable. In your example, for sure, the early returns can look more obvious to read. In many cases, they make people lose the flow and intention of the code.

My own preference would certainly depend upon the specific case under consideration, but this was a piece of general advice I have received from many throughout my journey.

Anyway, the main idea of mentioning early returns in the article was to explain that they exist and can be used as an exit from a function (as a function output), but if they complicate the readability too much, it's better to have a single return.

Even my Pokémon knowledge is rusty now. Looking forward to the Diamond and Pearl remakes, and Pokémon Legends. :D

Hope you'll stick around for more! Take care!

Collapse
marwaneb profile image
Marwan El Boussarghini

Thanks for the answer, I agree we always need to be cautious about readability !
I always like to challenge my knowledge and the things I do without even thinking about them, it's cool to read some interesting articles that help me do so 👍🏽

Collapse
larsonnn profile image
Lars Feldeisen

Pure functions have no side effects yes. That does not mean that "implicit output" is a side effect. Pure functions have as first rule: the same input gives the same output. (That's the important part)

Implicit outputs is also not a kind of style. it's just lazy. And a bad programming. You working with globals and that could be a real problem in real world. You should more clarify that, because for beginners it looks some kind of cool. And wow its easy. But its the opposit, the code get fast messy and working with other developers would be hard.

But realy nice you are writing about functional programming. And im looking forward to read more. You should go on: "Curry and Function Composition" in some of your next chapters. They are the real deal and helpful af.

Collapse
anmshpndy profile image
Animesh Pandey Author

Yep, thanks for mentioning that. I think I accidentally glossed over this, because the article was already getting too long.

I thought of covering Pure Functions and Side Effects very properly in the next one, after introducing relevant terms here in this one to aid in understanding the upcoming concepts.

Will be definitely going over Composition and Currying in a later one!

Thanks a lot for liking it!

Collapse
youpiwaza profile image
max

Waow, what a great article. I'm a teacher and this will greatly help some of my students which want to improve in JS.

Some returns to improve it :

  1. Prefer using let instead of var, new JS stuff :p
  2. You should differenciate logs & returns, and not using the same "output" term, in your examples ;)

Thanks a lot for this great work, have a nice day :)

Collapse
anmshpndy profile image
Animesh Pandey Author • Edited

Thanks! Glad you liked it!

And also thank you for your feedback. There was really no reason for me to prefer var over let, it was just my own habit. The outputs/returns could use some improvement though. I am looking into better ways to inject executable code into an article, so it could be of more help. Right now the best option seems to be CodePen. I will revisit and update all previous articles once I am satisfied.

Well, stay tuned for more! Thanks again!

Collapse
johnkazer profile image
John Kazer

Thanks, that was a good intro. JavaScript I've found is really a good way to learn functional programming. it allows you to slowly integrate the style into your day-to-day.

The funny thing is that the more I converted to using functional JavaScript the more I was able to understand a more completely functional language like Clojure. As ClojureScript is a variant that compiles to JavaScript and can use JavaScript libraries I am now wondering what really is the point anymore of writing JavaScript at all?

Collapse
anmshpndy profile image
Animesh Pandey Author

Haha, I once had to contribute to a project written in ClojureScript long ago, for a bounty. Back then, I didn't have much understanding of FP, so I was absolutely blown away! Eventually, I got the hang of it, and I'm looking forward to making something of my own out of it! But it was probably the steepest learning curve of my life, so far.

I was going to bring it up eventually, in one of the articles to come, about how exceedingly amazing FP implementation can be in ClojureScript, for someone coming from a JavaScript or similar background.

JavaScript on its own, in my opinion, has a sweet balance between imperative and declarative styles, hence quite amazing for learning any new concepts.

Thanks for liking the article! There's so much to cover under the umbrella of "fundamentals", it's no wonder many learners are flabbergasted when they are directly skipped over to some advanced FP concept.

Hope you'll like the next one too!

Collapse
pris_stratton profile image
pris stratton

Great article, looking forward to the next chapter!

I love FP - there's a great podcast by a guy called Eric Normand which is on Spotify which got me into it.

Collapse
anmshpndy profile image
Animesh Pandey Author

Thanks a lot! :)

Oh, I'll check the podcast out too. Maybe there'll be a reference from it next article?

Collapse
pris_stratton profile image
pris stratton

It’s called “Thoughts on Functional Programming” if you do search for it.

Thread Thread
anmshpndy profile image
Animesh Pandey Author

Yep, found it! Pretty good, thanks!

Of all development and programming-related platforms, I didn't think there'd be something like this on Spotify, haha! 😆

Collapse
ashleyo profile image
Ashley Oliver

a. Early Returns

There is a mistake in your test case. f(12) should clearly return 13.

Collapse
anmshpndy profile image
Animesh Pandey Author

Yep, you are right! Thanks a lot!

Collapse
davidyaonz profile image
David Yao

Very detailed explanation, even though FP has some of kind of steep learning curve, but it is definitely worth mastering if developers want to dive more. Thanks for the effort.

Collapse
anmshpndy profile image
Animesh Pandey Author

Glad you liked it! Thank you!

Collapse
thatanjan profile image
Anjan Shomodder

Great Explanation.

Collapse
anmshpndy profile image
Animesh Pandey Author

Thanks! Hope it helped just a little bit, atleast!

Collapse
rogjerrr profile image
DenVarenie

Thanks!

Collapse
anmshpndy profile image
Animesh Pandey Author

Thank you for reading!