Function Composition point-free style
A functional glimpse in JavaScript
What is a function?
A function is something that map an input to an output
// addOne is a function
function addOne(x){
return x + 1;
}// more like a procedure not a real function (Mathematics)
// not everything using function keyword is a real function
// this also doing a bad practice as we will see function something(){
console.log("i am a fake function");
}
now you know what is a real function, let's know more about functions in a functional programming world especially two important concepts :
1. First class functions:
When we say functions are “first class”, we mean they are just like everything else.
We can treat functions like any other data type and there is nothing particularly special about them — they may be stored in arrays, passed around as function parameters, assigned to variables, etc.
const hi = name => `Hi ${name}`; // as a normal value
const greeting = name => hi(name); // returned from a function
you may know about the concept callbacks or anonymous functions in JavaScript, these concepts existing because functions in javascript are first-class citizens like any other values
// callbacks
const getServerStuff = callback => ajaxCall(json => callback(json));// same as above
const getServerStuff = ajaxCall;
in pure functional programming languages like Haskell, everything is an expression even the program itself
2. Pure functions
A pure function is a function that, given the same input, will always return the same output and does not have any observable side effects
const xs = [1,2,3,4,5];// impure
xs.splice(0,3); // [1,2,3]
xs.splice(0,3); // [4,5]// pure
xs.slice(0,3); // [1,2,3]
xs.slice(0,3); // [1,2,3]
// impure
const minimum = 21;
const checkAge = age => age >= minimum;
// pure
const checkAge = (age) => {
const minimum = 21;
return age >= minimum;
};
Side effects may include, but are not limited to
- changing the file system
- inserting a record into a database
- making an HTTP call
- mutations
- printing to the screen/logging
- obtaining user input
- querying the DOM
- accessing system state
you may wonder what is the purpose of a program that doesn't change anything …. well, there are ways to do such thing in a pure way like IO Monads
so now you know what is a function let’s talk about another important thing that plays a key role in function composition
Function Currying:
The concept is simple: You can call a function with fewer arguments than it expects. It returns a function that takes the remaining arguments.
const add = x => y => x + y;
const increment = add(1);
const addTen = add(10);
increment(2); // 3
addTen(2); // 12
in languages like Haskell all function are automatically curried, but let write our first functional utility curry
it takes any function to convert it to a curried function this is possible in javascript because of recursion
and closures
function curry(fn) {
const arity = fn.length;
return function $curry(...args) {
if (args.length < arity) {
return $curry.bind(null, ...args);
}
return fn.call(null, ...args);
};
}
libraries like lodash
or ramda
include these utilities so you don't need to write it yourself, so let use it
const match = curry((what, s) => s.match(what));
const replace = curry((what, replacement, s) => s.replace(what, replacement));
const filter = curry((f, xs) => xs.filter(f));
const map = curry((f, xs) => xs.map(f));
a curried function is the concept that allows us to partially apply a function
In math calling a function is called “Applying a function” so partially calling function with fewer params called “Partial Application” and it is a common concept in functional world
oh, beautiful isn't it, now let's talk about composting of functions
Function Composition:
a composition is mixing more than one small function to make a bigger one
const toUpperCase = x => x.toUpperCase();
const exclaim = x => `${x}!`;const shout = x => toUpperCase(exclaim(x))
shout('send in the clowns'); // "SEND IN THE CLOWNS!"
so if you have a function f
and function g
composing them like g . f
, the dot means f after g
and these must be associative g.f === f.g
that means it doesn't matter if you call f
first or g
first, they should be the same and functions bowered this property from functions in math, now let write another utility to make composition easier compose
function compose(...fns) {
const n = fns.length;
return function $compose(...args) {
let $args = args;
for (let i = n - 1; i >= 0; i -= 1) {
$args = [fns[i].call(null, ...$args)];
}
return $args[0];
};
}
compose function takes any number of functions and compose them from right to left
const head = x => x[0];
const reverse = reduce((acc, x) => [x].concat(acc), []);
const last = compose(head, reverse);
last(['jumpkick', 'roundhouse', 'uppercut']); // 'uppercut'
there are other shapes of compose composeLeft
, composeRight
you can find them in libraries we mentioned before
Pointfree:
Pointfree style means functions that never mention the data upon which they operate. First class functions, currying, and composition all play well together to create this style.
// not pointfree because we mention the data: word
const snakeCase = word => word.toLowerCase().replace(/\s+/ig, '_');
// pointfree
const snakeCase = compose(replace(/\s+/ig, '_'), toLowerCase);
Let’s look at another example.
// not pointfree because we mention the data: name
const initials = name => name.split(' ').map(compose(toUpperCase, head)).join('. ');// pointfree
const initials = compose(join('. '), map(compose(toUpperCase, head)), split(' '));initials('hunter stockton thompson'); // 'H. S. T'
we just scratched the surface of composing functions and you may wonder where all of these rules and concepts come from, off-course it rooted by a mathematical theory and that math proved for hundreds of years before even computers math areas like Lambda calculus, Category theory is one of the main roots behind our beautiful world of functions
Reference: