JavaScript the Fun Part


functional-programming-javascript

pure-and-impure-functions


So far we learned about

  1. function as first class citizen
  2. closure
  3. currying
  4. partial application
  5. composition

The last part of this list, composition works because of pure functions. Functions that we used in previous post for sorting a list of words are all pure functions.

R.pipe(
    fs.readFileSync,                // read the file
    toString,                       // convert it to string
    R.split( newLine ),             // array of lines
    R.map( lineToWord ),            // array of array of words
    R.flatten,                      // flatten it, array of array to array
    R.filter( ignoreEmtpyItem ),    // remove empty items
    R.sort( sortByLength ),         // sort by item's length
    R.map( mapLengthToString ),     // a single string for each item
    R.reduce( arrayToString, "" ),  // everything to a single string
    log                             // print
)( filename );

One can ask, how it is possible? How a program can create result without any side-effect? And it is time to clarify what is a pure function.

A pure function

The shortest definition for a pure function is: a function that has no side-effect. And it quickly becomes confusing that if all functions have no side-effect how can we produce the result?

The reality about a pure function is that it can have and only internal side effects and no external side effects.

Here is a function that do not have any side effect.

const add = a => => a + b;

This function just returns a result of an addition operation and we know that addition operation is a side effect free operation.

This one has side effect on its input, but not on external states, since the input is not by reference, we can ignore it, thus it is pure.

const inc = a => ++a;

And here is a more famous utility that has internal side effects but no external.

const filter = function( predicate, array ){
    const newArray = [];
    for( const index of array )
        if( predicate( array[ index ] ) )
            newArray.push( array[ index ] );

    return newArray;
}

And since we are familiar with curry functions it is better to write it this way.

const filter = function( predicate ){
    return function( array ){
        const newArray = [];
        for( const index of array )
            if( predicate( array[ index ] ) )
                newArray.push( array[ index ] );
        return newArray;
    }
}

It has an array (= newArray). It modifies it but it is hidden from our application and we cannot see it nor care about it. We give it an input, and it gives us back an output.

So we can say these about a pure function

So functions that do not satisfy these rules are considered as impure functions, functions that have side effects.

Completely side effect free program

Anyone that has coded for a while immediately think that how is it possible to code with no side effect? And it is true that is not possible to have a 100% side effect free software and we need to change some states.

In fact the emphasize is on having less impure functions and think of it to solve our problem using pure functions as much as possible and not having no side effect at all. Having less impure functions leads us to have less side effects which itself leads us to

State and state management

By a simple observation over all pure functions or the rules we reviewed we can see that a function either does not have a state or it has. In case of having a state, a function, manages its own states and does not care about outside world a kind of self state management.

Following the rule of self-state management enables us to combine some of these pure function known as composition to achieve our final needs.

So without pure functions, composition does not make sense, and many useful pure functions like

have a kind of isolate states that do not affect outside of the function.

Low coupling and High cohesion

While these two terms mostly is used in OOP, but we can see them in FP, too. By having pure functions, in fact we do not have coupling between functions.

Here is an example of high coupling (= impure functions) that makes hard to debug, test, and compose our functions

// data 
let year = 2019;

// impure function
function inc(){
    ++year;
}

// impure function
function dec(){
    --year;
}

function* random01(){
    while( true )
        yield Math.floor( Math.random() * 2);
}

let max = 10;
const iterator = random01();

while( max-- ){
    const { value } = iterator.next();
    if( value )
        inc();
    else
        dec();
}

console.log( year );    // what is your best guess?  

Yes, it is unpredictable! And imagine it for a large application with many functions.

Comparing to this one

let year = 2019;

// pure function
function inc( x ){
    return ++x;
}

// pure function
function dec( x ){
    return --x;
}

function* random01(){
    while( true )
        yield Math.floor( Math.random() * 2);
}

let max = 100;
const iterator = random01();

while( max-- ){
    const { value } = iterator.next();
    if( value )
        inc( year );
    else
        dec( year );
}

console.log( year );    // what is your best guess?

Because our inc, and dec functions have no side effects, we can predicate that "year" variable is still 2019.

Explicit is better than implicit

This is from python zen coding. Python fans usually criticizes Perl or other languages for some implicit behavior. So python is said to be explicit about its behavior. Using this rule we can see that while we did not change our year variable, we were not explicit about it while we could

const year = 2019;

Using constant assignment makes is explicit that it should not be modified, also prevent our data from accidental changes by others.

This concept of constant and non-constant data leads us to our next post which is will be about immutable & mutable data.


Update: Fri Nov 08 2019 22:21:59 GMT+0330 (Iran Standard Time)