Functional First

fn.js aims to support functional practices over performance or established conventions. In order to guide you on the right path, fn.js internals force avoidance of side effects, object mutation, and function state.

Run Everywhere

fn.js runs in the Node.js or in the browser using normal script tags or via an AMD loader such as RequireJS. It's also available in Bower.

Open Source

fn.js is MIT licensed and hosted on GitHub. Bug reports, feature recommendations, and pull requests are encouraged and welcomed.


Installation


Browser - Script Include

<script src="path/to/fn.js"></script>
<script>
	// fn now available
</script>

Browser - AMD/RequireJS

require.config({
	paths: {
		fn: 'path/to/fn'
	}
});
define(['fn'], function (fn) {
	// fn now available
});

Bower

# make sure to put fn.js, NOT just fn
$ bower install fn.js

# now use one of the browser inclusion methods above

Node.js

# make sure to put fn.js, NOT just fn
$ npm install fn.js
var fn = require('fn.js');
// fn now available


Introduction

fn.js is an opinionated JavaScript library that pushes you to to adopt strategies that encourage a functional programming style. I do not purport to be a master of functional programming especially as it relates to mathematical concepts, and I do not expect you to be either. Rather, fn.js provides functionality that you may be used to in Underscore.js or Lodash, but in a way that lends itself to greater self-composability and support of core functional programming tenets.

Core Functional Programming Tenets

Avoidance of Side Effects

The concept of side effects is closely related to the design pattern of the Single Responsibility Principle, or as I like to say: "A function should do one thing, and do it well." Any function that does something other than its single intended purpose is said to have side effects. In pure functional programming languages side effects are not allowed, but this is JavaScript and everything is fair game and must be enforced with conventions. fn.js prevents side effects from function context state, e.g. this, by never applying a function's context upon execution. This may cause some pain points for code that is already heavily object-oriented or relies a lot on prototypes. Instead, fn.js encourages you to provide a function's data by passing it as arguments, thereby avoiding side effects.

Referential Transparency

In object-oriented or classic programming applications, the flow of code is controlled by creating objects that mimic real-world or virtual beings. As an example, if you were creating an application that allowed users to purchase a vehicle, you might create a Vehicle class or prototype to model the interaction that a user performs in customizing their vehicle. As a Vehicle instance is modified, other functionality in an application is triggered to execute, and so continues the flow.

In order to control the flow of the program, you must modify the state of objects or the state of an application. Different outcomes are possible based on the state of those objects or the application. In this manner, we can say that object-orientation lacks referential transparency.

Referential transparency is the ability of code to be evaluated in a predictable manner, not influenced by external state. Code that has achieved referential transparency is only affected by arguments that are passed to it, and changing state in the application has no effect. That same code should always output the same value for a given set of arguments, and only changing those arguments can a value be different.

In object-orientation, changing the state of the Vehicle can change how functionality on the rest of the site works. In functional programming, we create functions that reads nothing external, only arguments passed to it.

Immutability

Immutability, or the inability for variables to changes their values once created. In other words, all things created should strive to be constants. In JavaScript there is no internal mechanism to enforce this as there is in pure functional programming languages like Haskell and friends. JavaScript is dynamic and without strict typing, and so keeping variables immutable in a codebase is merely a convention and cannot be enforced by fn.js. With that being said, fn.js strives to adhere to this rule and all methods used should treat all arguments passed as immutable. For example, using .map or .reverse will give you back new arrays, not mutate your existing arrays.

Higher-Order Functions

Functional programming is made possible through the use of higher-order functions. A higher-order function is a function that can accept functions as arguments, and can even return a function. Higher-order functions facilitate some interesting techniques:


Documentation, 0.8.1


Description Convert an array-like value to an actual array. Useful for converting things like the arguments object, NodeLists, etc. to arrays. This will then allow usage of native array methods on the new collection.
Arguments
collection mixed (array-like value) The array-like collection to convert to an array
Returns Array
Example
var echo = function () {
	return fn.toArray( arguments );
};

echo( 1, 2, 3 ); // [ 1, 2, 3 ]
Description An alias to the fn.toArray method. Merely serves as a semantic method for conveying intent when copying arrays.
Arguments
array Array The array to be copied
Returns Array
Example
var names = [ 'Bill', 'Jenn', 'Doug' ];
	fn.cloneArray( names ); // [ 'Bill', 'Jenn', 'Doug' ];
Description Retrieves a handy usable string type for any value. Note that this does not use the typeof operator, but rather the lowercase version of its true underlying name. Also works on null and arrays.
Arguments
value mixed (any value) The value to fetch the type for
Returns String Lowercase true internal type representation
Example
fn.type( 'Bill' ); // "string"
fn.type( 3 ); // "number"
fn.type( {} ); // "object"
fn.type( [] ); // "array"
fn.type( function () {} ); // "function"
fn.type( null ); // "null"
fn.type( undefined ); // "undefined"
fn.type( /Hello World/i ); // "regexp"
fn.type( new Date() ); // "date"
Description Given a string type, determine whether a given value matches the type.
Arguments
type String The string type to compare against the supplied type of the value. Note that this string type is the same format as the ones returned by fn.type.
value mixed (any value) The value to fetch the type for
Returns Boolean: true if the value is of the supplied type, otherwise false
Example
fn.is( 'string', 'Bill' ); // true
fn.is( 'undefined', 3 ); // false
fn.is( 'object', {} ); // true
fn.is( 'function', [] ); // false
fn.is( 'function', function () {} ); // true
fn.is( 'undefined', null ); // false
fn.is( 'null', undefined ); // false
fn.is( 'regexp', /Hello World/i ); // true
fn.is( 'number', new Date() ); // false
Description Apply an array of values as individual arguments to the supplied handler function.
Arguments
handler Function The function to have arguments applied to
args Array The collection of values to apply individually as arguments to the handler
Returns The return value of the handler function.
Example
var fullname = function ( firstName, lastName ) {
	return firstName + ' ' + lastName;
};

fn.apply( fullname, [ 'Bill', 'Smith' ] ); // "Bill Smith"
Description Combine all supplied array arguments into a single array.
Arguments
arrays Array Each argument is an individual array to be merged with the other supplied arrays.
Returns Array A single array of all supplied arguments.
Example
fn.concat( [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ); // [ 'a', 1, 'b', 2, 'c', 3 ]
Description Return a new version of the handler function whose supplied arguments have been pre-filled.
Arguments
handler Function The function which will have partially applied arguments.
Optional args Any Each argument is a value which will be pre-applied to the given handler function.
Returns Function A new function with supplied arguments pre-applied.
Example
var fullName = function ( firstName, lastName ) {
	return firstName + ' ' + lastName;
};

var billName = fn.partial( fullName, 'Bill' );

billName( 'Smith' ); // "Bill Smith"
billName( 'Clinton' ); // "Bill Clinton"
Description Continuously partially apply a handler function until it receives all its expected requirements before invoking. Any remaining arguments will be passed on at invocation. Currying is dependent on being able to read how many arguments a function expects by having the declared with the handler, e.g function (a, b, c) {} will curry until it has received its 3rd argument. If the arguments declared with the function are not in line with what the function internally required, specify the arity argument.
Arguments
handler Function The function which will have its arguments curried.
Optional arity Number How many arguments should be supplied before invoking the handler
Returns Function A new function which curries arguments until the the required amount has been met.
Example
var fullName = fn.curry(function ( firstName, middleName, lastName ) {
	return firstName + ' ' middleName + ' ' + lastName;
});

var billName = fullName( 'Bill' );

billName( 'Damon' )( 'Smith' ); // "Bill Damon Smith"
billName( 'Jefferson', 'Clinton' ); // "Bill Jefferson Clinton"
fullName( 'Jenn', 'Anne', 'Cochran' ); // "Jenn Anne Cochran"
fullName( 'Jenn', 'Anne' )( 'Cochran' ); // "Jenn Anne Cochran"
Description The right-associative version of fn.curry. Continuously partially apply a handler function until it receives all its expected requirements before invoking. Any remaining arguments will be passed on at invocation. Currying is dependent on being able to read how many arguments a function expects by having the declared with the handler, e.g function (a, b, c) {} will curry until it has received its 3rd argument. If the arguments declared with the function are not in line with what the function internally required, specify the arity argument.
Arguments
handler Function The function which will have its arguments curried.
Optional arity Number How many arguments should be supplied before invoking the handler
Returns Function A new function which curries arguments until the the required amount has been met.
Example
var fullName = fn.curryRight(function ( firstName, middleName, lastName ) {
	return firstName + ' ' middleName + ' ' + lastName;
});

var smithName = fullName( 'Smith' );

smithName( 'Damon' )( 'Bill' ); // "Bill Damon Smith"
smithName( 'Jefferson', 'Bill' ); // "Bill Jefferson Clinton"
fullName( 'Cochran', 'Anne', 'Jenn' ); // "Jenn Anne Cochran"
fullName( 'Cochran', 'Anne' )( 'Jenn' ); // "Jenn Anne Cochran"
Description Create an array of all the own properties/keys of an object.
Arguments
object Object The object for which to fetch the keys.
Returns Array An array of string keys.
Example
var props = fn.properties( { firstName: 'Julie', lastName: 'Master' } ); // [ "firstName", "lastName" ]
Description Execute a function for each value in the supplied collection. Optionally provide an array of additional arguments to be applied and passed into the function.
Arguments
handler Function( currentValue, index, collection, ...params ) The function to execute for each value. This function will be passed the current item for the iteration, its index in the collection, the array collection itself, and any optionally supplied arguments.
collection Array The collection to iterate over.
Optional params Array An array of additional arguments to pass to handler.
Returns Nothing is returned from this method.
Example
fn.each( function ( value ) {
	console.log( value );
}, [ 'Jim', 'Elijah', 'Jenn', 'Julie' ] );
// "Jim"
// "Elijah"
// "Jenn"
// "Julie"
fn.each( function ( value, index, collection, additional1, additional2 ) {
	console.log( value ); // "Jim"
	console.log( index ); // 0
	console.log( collection ); // [ 'Jim', 'Elijah', 'Jenn', 'Julie' ]
	console.log( additional1 ); // "optional"
	console.log( additional2 ); // "arguments"
}, [ 'Jim', 'Elijah', 'Jenn', 'Julie' ], [ 'optional', 'arguments' ] );
Description Reduce a collection to a single value by processing each value in the collection through the handler function. Current reduction progress is tracked with the accumulator argument.
Arguments
handler Function( accumulator, index, ...params ) The function to execute for each value. This function will be passed the accumulator, current item for the iteration, its index in the collection, and any optionally supplied arguments. Return a value from this function to set the accumulator for the next function invocation.
accumulator Any A value used to accumulate reduction values between function passes.
collection Array The collection to iterate over.
Optional params Array An array of additional arguments to pass to handler.
Returns The final value of the accumulator.
Example
fn.reduce( function ( accumulator, value, index ) {
	return accumulator + value + index;
}, 0, [ 1, 2, 3 ] );
// 9
Description Create a new array from the supplied collection by passing each value through a filtering expression. If the expression returns truthy, the value will be placed into the new collection.
Arguments
expression Function( currentValue, index, ) The function to execute for each value in the collection. Returning a truthy value from the expression will place the currentValue into the new array, NOT the returned value.
collection Array The collection to iterate over.
Returns Array A new array containing the filtered results of the original collection.
Example
fn.filter( function ( value ) {
	return value % 2 === 0;
}, [ 0, 1, 2, 3, 4, 5, 6 ] );
// [ 0, 2, 4, 6 ]
Description Create a new array from the supplied collection by returning a new value from each iteration of the handler function.
Arguments
handler Function( currentValue, index, collection, ...params ) The function to execute for each value in the collection. Return any value from the function to have it placed into the new array. This function will be passed the current iteration value, the index of that value in collection, the original collection, and any applied arguments from params.
collection Array The collection to iterate over.
Optional params Array An optional array of values to apply as additional arguments to the handler function.
Returns Array A new array containing a set of values returned from the handler function.
Example
fn.map( function ( value ) {
	return value * 2;
}, [ 0, 1, 2, 3, 4, 5, 6 ] );
// [ 0, 2, 4, 6, 8, 10, 12 ]
Description Create a new array with the contained values in the reverse order.
Arguments
array Array The array to reverse.
Returns Array A new array containing reversed values.
Example
fn.reverse( [ 0, 1, 2, 3, 4 ] ); // [ 4, 3, 2, 1, 0 ]
Description Create a new function that will compose and invoke functions from right-to-left, passing the return values from each invocation to the next successive function.
Arguments
...functions Function Each argument is a function that should be called when the new function is invoked. Functions are executed from right-to-left.
Returns Function A new function composed of the functions arguments. Any arguments passed to this new function will be passed to the first function on the right, and each successive function will receive the return value of the previous function invocation.
Example
var addSalutation = function ( name ) {
	return 'Grand Master ' + name;
};

var fullName = function ( person ) {
	return person.firstName + ' ' + person.lastName;
};

var logPerson = fn.compose( console.log, addSalutation, fullName );

logPerson( { firstName: 'Abe', lastName: 'Lincoln' } ); // logs: "Grand Master Abe Lincoln"
Description The left-associative version of fn.compose. Works identically except that functions are invoked from left-to-right
Arguments
...functions Function Each argument is a function that should be called when the new function is invoked. Functions are executed from left-to-right.
Returns Function A new function composed of the functions arguments. Any arguments passed to this new function will be passed to the first function on the left, and each successive function will receive the return value of the previous function invocation.
Example
var addSalutation = function ( name ) {
	return 'Grand Master ' + name;
};

var fullName = function ( person ) {
	return person.firstName + ' ' + person.lastName;
};

var logPerson = fn.pipeline( fullName, addSalutation, console.log );

logPerson( { firstName: 'Abe', lastName: 'Lincoln' } ); // logs: "Grand Master Abe Lincoln"
Description Fetch the value from an object matching the specified key/property name. IMPORTANT NOTE: this method is already curried, so you can just provide name and receive a new function awaiting the property.
Arguments
name Any The object or primitive from which to fetch the key/property value.
Returns Mixed. Returns the value contained within the specified object.
Example
fn.prop( 'firstName', { firstName: 'Abe', lastName: 'Lincoln' } ); // "Abe"
var getFirstName = fn.prop( 'firstName' );

getFirstName( { firstName: 'Abe', lastName: 'Lincoln' } ); // "Abe"
getFirstName( { firstName: 'George', lastName: 'Washington' } ); // "George"
Description Merge the properties and values from all supplied objects into a new object. Properties contained in objects on the right will overwrite properties of objects on the left. Note that this is not a deep merge, and that a new object is returned.
Arguments
...objects Object Each argument is an object which will have its properties copied into the new object. Objects on the right have a high property-priority than those on the left.
Returns Object Returns a new object which is a shallow merge of all supplied objects.
Example
fn.merge( { firstName: 'Abe' }, { lastName: 'Lincoln' }, { favoriteAnimal: 'Velociraptor' } );
// { firstName: "Abe", lastName: "Lincoln", favoriteAnimal: "Velociraptor" }
Description Return a new function that caches invocations of the handler. The default serializer uses a combination of value types and JSON.stringify is used to determine cache keys, but an optional serializing function can be provided for custom serialization
Arguments
handler Function The function for which execution caching is being performed.
Optional serializer Function( args ) The function which generates cache keys. The serializer function is passed a single array which contains all the arguments passed to the handler.
Returns Function Returns a new function that caches duplicate invocations.
Example
var cached$ = fn.memoize( jQuery );
// First pass will go directly to jQuery
cached$( '.container' );

// Using the same selector again will return the exact same object as before,
// without hitting the DOM again
cached$( '.container' );
Description Return a new function which accepts arguments in the reverse order that the original handler specifies.
Arguments
handler Function A function which needs to accept arguments in reverse order. The arguments will still be injected into this function in the left-to-right order.
Returns Function Returns a new function that accepts arguments in the reverse order of handler.
Example
var displayName = function ( firstName, lastName ) {
	return firstName + ' ' + lastName;
};

var flippedDisplayName = fn.flip( displayName );

displayName( 'Abe', 'Lincoln' ); // "Abe Lincoln"
flippedDisplayName( 'Lincoln', 'Abe' ); // "Abe Lincoln"
Description Invoke a function after msDelay milliseconds. Note that this will yield to the event loop and be invoked after other statements.
Arguments
handler Function A function to invoke after a delay
msDelay Number The number of milliseconds to wait before invoking the handler.
Returns Number A number representing the ID of the delayed handler. Handler invocation be prevented with clearTimeout( number ).
Example
fn.delay( function () {
	console.log( 'Aliens...' );
}, 500 );
// logs: "Aliens" after 500 milliseconds
Description Invoke a function after msDelay milliseconds. Note that this will yield to the event loop and be invoked after other statements. This is the flipped version of fn.delay.
Arguments
msDelay Number The number of milliseconds to wait before invoking the handler.
handler Function A function to invoke after a delay
Returns Number A number representing the ID of the delayed handler. Handler invocation be prevented with clearTimeout( number ).
Example
fn.delay( 500, function () {
	console.log( 'Aliens...' );
});
// logs: "Aliens" after 500 milliseconds
Description Returns a new function that will always invoke the handler after msDelay milliseconds.
Arguments
handler Function A function to invoke after a delay.
msDelay Number The number of milliseconds to wait before invoking the handler.
Returns Function A new function that always executes after the specified amount of time. This function will have its arguments applied to the handler upon invocation.
Example
var notifyAliens = fn.delayed( function () {
	console.log( 'Aliens...' );
}, 500 );

// These functions are always invoked after a 500ms delay
notifyAliens();
notifyAliens();
notifyAliens();
Description Returns a new function that will always invoke the handler after msDelay milliseconds. Note that this will yield to the event loop and be invoked after other statements. This is the flipped version of fn.delayed.
Arguments
msDelay Number The number of milliseconds to wait before invoking the handler.
handler Function A function to invoke after a delay.
Returns Function Returns a new function that is always invoked after the specified amount of time.
Example
var notifyAliens = fn.delayedFor( 500, function () {
	console.log( 'Aliens...' );
});

// These functions always invoke after a 500ms delay
notifyAliens();
notifyAliens();
notifyAliens();
Description Returns a new function that will always defer execution of the handler to the end of the event loop.
Arguments
handler Function A function to invoke after yielding to the event loop.
Returns Function Returns a new function that is always invoked after other queued statements in the event loop.
Example
var asyncLog = fn.async( console.log );
asyncLog( 'Invalid username or password.' ); // logs after the event loop has yielded.
Description Returns a new function that can only be invoked once within msDelay milliseconds. Any calls that occur before the allotted time has passed are disregarded. The handler will be invoked at the end of the delay.
Arguments
handler Function A function for which invocations are throttled.
msDelay Number The amount of time in milliseconds to wait between handler invocations.
Returns Function Returns a new function that disregards invocations that occur before the specified delay has passed.
Example
$( window ).on( 'resize', fn.throttle( function () {
	console.log( 'Window resized!' ); // will only log once every 100ms while the window is resizing
}, 100 ));
Description Returns a new function that can only be invoked once within msDelay milliseconds. Any calls that occur before the allotted time will cause the timer to restart. The handler is invoked at the end of the delay
Arguments
handler Function A function for which invocations are debounced.
msDelay Number The amount of time in milliseconds to wait before a handler can be invoked.
Returns Function Returns a new function that defers execution until the specified amount of time has passed. Any calls before the timer has passed will cause the timer to reset.
Example
$( document ).on( 'keypress', fn.debounce( function () {
	// only allow the key to be pressed every 200ms. This handler will continuously
	// defer itself until the key has been quiet for 200ms.
}, 200));