JavaScript the Fun Part


functional-programming-javascript

composition


After knowing about

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

It is time to put all of these in to practice. Composition is what lets us using these four techniques.

Lets create two curry functions: addition and multiplication in ES5 style.

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

By now you should know that those arrow functions (= add, mul) are equivalent to these.

const add = function( a ){
    return function( b ){
        return a + b;
    }
}

const mul = function( a ){
    return function( b ){
        return a * b;
    }
}

And these also are equivalent to these

function add( a, b ){
    function aPlusb(){
        return a + b;
    }
    return aPlusb();
}

function mul( a, b ){
    function aTimesb(){
        return a * b;
    }
    return aTimesb();
}

And since we want to use partial application we like to have a curry function, which is some consecutive nested unary functions.

applying arguments partially

Now that we have two curry functions, lets use them

 // just binding first argument
const add5 = add( 5 );  // returning a function
const mul2 = mul( 2 );  // returning a function

our compose function

We can do our composition in two ways

  1. compose: right to left composition
  2. pipe: left to right composition
// compose: right to left
const compose = ( f1, f2 ) => arg => f1( f2( arg ) );

// pipe: left to right composition
const pipe = ( f1, f2 ) => arg => f2( f1( arg ) );

composing add5, mul2

const c = compose( add5, mul2 ); // returning a function that accepts one argument
const p = pipe( mul2, add5 );    // returning a function that accepts one argument

What is it returning back? This part

// for compose, it is assigned to "c"
// arg => f1( f2( arg ) )

// for pipe, it is assigned to "p"
// arg => f2( f1( arg ) );

getting the result

// compose result
const cr = c( 2 );  // equals to add5( mul2( 2 ) )
const pr = p( 2 );  // equals to add5( mul2( 2 ) )

console.log( cr );  // 9 === ( 5 + ( 2 * 2 ) )
console.log( pr );  // 9 === ( 5 + ( 2 * 2 ) )

using Ramda.js

We do not have to create a compose, or pipe function. We can use a library that has been tested many times.

const R = require( "ramda" );

// Two curry functions in ES6 syntax
const add = a => b => a + b;
const mul = a => b => a * b;

// partial application over add, and mul
const add5 = add( 5 );  // 5 + b
const mul2 = mul( 2 );  // 5 * b

// using R.compose
const cr = R.compose( add5, mul2 )( 2 );
console.log( 'cr', cr );    // cr 9

// using R.pipe
const pr = R.pipe( mul2, add5 )( 2 );
console.log( 'pr', pr );    // pr 9

Learn English Effectively

I already created a node.js script for sorting a file based an word's length.

Imaging you are learning English. One of good techniques to learn words is to start from small words, words that have shorter length.

For example if you already know about these three

  1. ploy
  2. morph
  3. -ism

then you have a better chance to understand the word polymorphism. Why? Because your mind is already familiar with smaller parts of this word.

Here is the code I wrote a few months ago, it is a script; almost functional but not quite right.

#!/usr/bin/env node

const fs = require( "fs" );

const log = console.log;
const program = process.argv[1].split( /\// ).pop();
const filename = process.argv[ 2 ];

if( !filename ){
    log( `usage: ${program} filename` );
    log( `Ex.1 : ${program} file.txt` );
    process.exit( 0 );
}


const file = fs.readFileSync( filename );
const fileString = file.toString();
const fileLines = fileString.split( "\n" ).filter( ( line ) => line !== "" );

let unique = {},
    words = [];
fileLines.forEach( function( line ){
    words = line.split( /\W+|\d+/g ).filter( ( word ) =>  word !== "" );
 
    words.forEach( function( word ){
        unique[ word ] = word.length;
    })
})

const sprintf = functions( text, space = 5 ){
    text = text + "";
    return ( text + ( " ".repeat( space - text.length ) ) );
}

let value = 0;
const result =
Object.keys( unique )
       .sort( ( a, b ) => { return unique[ a ] - unique[ b ] } )
       .reduce( function( accum, key ) {
            value = unique[ key ];
            return accum + sprintf( value ) + key + "\n";
            // log( sprintf( value ), key );
        }, "");
log( result );

And here is the code after refactoring using Ramda.js.

#!/usr/bin/env node

const fs = require( "fs" );
const R = require( "ramda" );

const log = console.log.bind( console );
const program = process.argv[1].split( /\// ).pop();
const filename = process.argv[ 2 ];

if( !filename ){
    log( `usage: ${program} filename` );
    log( `Ex.1 : ${program} file.txt` );
    process.exit( 0 );
}

const newLine = "\n";
const toString = string => string.toString();
const ignoreEmtpyItem = item => item !== "";
const lineToWord = line => R.split( /\W+|\d+/g, line );
const sortByLength = ( a, b ) => a.length - b.length;
const mapLengthToString = string => string.length + " " + string;
const arrayToString = ( result, input ) => result + input + "\n";

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

So with this post, and four previous ones, we learned about

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

We will continue our discussion about functional programming in next posts.


Update: Wed Nov 06 2019 08:31:13 GMT+0330 (Iran Standard Time)