Project Tin Functional Programming and Utility Functions
npm install sn-coresn-core
=======
Project Tin Functional Programming and Utility Functions
This module provides some basic functional programming and cross-environment
utilities.
Functional Programming Support
------------------------------
This is module adds a few functional programming primitives. They're no better
than ones provided by other packages, they're just in a format I like to use.
This module adds:
Function.prototype._$partial( args ) - this function returns a function
which pre-applies some number of arguments. Here's an example cribbed from
http://dailyjs.com/2012/09/14/functional-programming/ :
function add(x,y) { return x+y; };
var add_three = add.$_partial(3);
console.log( add_three(4) ); // this should print the number 7Function.prototype._$compose( function ) - this function returns a function
which executes the base function (the thing you do the _$compose() on) on the
output of the function you pass as a parameter to _$compose(). Example:
function add2( x ) { return 2+x; }
function mult2( x ) { return 2*x; };
var add_then_mult = mult2._$compose( add2 );
console.log( add_then_mult( 7 ) ); // should print the number 18Function.prototype._$flip() - returns a function whose arguments are
reversed.
function div(x,y) { return x/y; }
var recip = div._$flip();
console.log( div( 10, 5 ) ); // should print the number 2
console.log( recip( 10, 5 ) ); // should print the number 0.5Function.prototype._$negate() returns a function whose value is
the logical negation of the original.
function isTwo( x ) { return 2 == x; }
var isNotTwo = isTwo._$negate();
console.log( isTwo( 1 ) ); // should print false
console.log( isTwo( 2 ) ); // should print true
console.log( isNotTwo( 1 ) ); // should print true
console.log( IsNotTwo( 2 ) ); // should print falseArray.prototype._$each(function) and Object.prototype._$each(function)
Iterates through array calling the function passed for each value passing
the element and the index as parameters. 'this' is set as the array
(or object).
Array.prototype._$map(function) and Object.prototype._$map(function)
Iterates through each element of the array (or object), creating a new array
(or object) whose elements are the contents of the original array passed
through the function provided.
Object.prototype._$fold( function, base ) Iterates through the object's members
combining the output of the value returned from the function provided with the
base value specified.
Array.prototype._$reverse() Returns a shallow copy of the reciever with it's
elements reversed. (Unlike Object.reverse(), it doesn't modify the receiver.)
Object.prototype._$all( function ) Returns true if the function provided returns
true when each of the members of the collection are passed as a parameter.
Object.prototype._$any( function ) Returns true if the function provided returns
true when at least one of the members of the collection are passed as a parameter.
Object.prototype._$none( function ) Returns true if the function provided returns
a false value when each of the members of the collection are passed as a parameter.
Object.prototype._$pluck( property ) Assumes the receiver is a collection of objects
and that each of those objects contains the property named as an argument to the pluck
call. The function returns an array of those properties plucked from the objects in
the collection.
var example = [
{ status: 200, body: "whatever" },
{ status: 201, body: "yeah. you're supposed to return a 201 when you create a new resource" },
{ status: 404, body: "not exactly found" }
];// this will return the array [200, 201, 404]
example._$pluck( 'status' );
Function.prototype._$f() This is a dummy function for when you need something that's
definitely a function, but you don't want to really do anything. I sometimes use this
function to represent default null behaviors so i don't have to check if a function is
defined.
function _do_something( callback ) {
var _callback = ('function'==typeof callback)?callback:_$f; // but look at _$g() below
// do a whole bunch of stuff here return _callback();
}
Function.prototype._$g( function ) If the parameter passed is a function, that function
is returned. If it's not a function, _$f is returned. Useful for guaranteeing callbacks
are functions (instead of undefineds or objects).
function _do_something_else( callback ) {
// do a whole bunch of stuff here return _$g( callback )();
}
Utility Functions
-----------------
Function.prototype._$punch( target, name ) Duck-punches a function into an existing
object, and makes it non-enumerable, non-configurable and non-writable.
( function( _f ) {
for( var i in this ) {
_f.apply( this, this[ i ] );
}
} )._$punch( Object.prototype, '_$sortOfLikeTheEachFunction' );{ a: "1", b: "2" }._$sortOfLikeTheEachFunction( function( e ) {
console.log( "wubba! " + e );
} );
Object.prototype._$shallow( source ) (shallow) copies properties
from the source object into the receiver. This is a "shallow" copy of the first
level of properties. This doesn't create a new object, so properties
that are in the receiver, but aren't in the source are left alone.
var a = { a: { whatever: "dingo" }, b: 2, c: 3 };a._$shallow( { a: { larb: "foo" }, z:26 } );
// a now looks like: {a: { larb: "foo" }, b: 2, c:3, z: 26}
Object.prototype._$merge( source ) Performs a recursive copy from
the source into the receiver. This is more like a "deep" copy. But like _$shallow,
it doesn't create a new object, so properties that are in the receiver, but aren't
in the source are left alone.
var a = { a: { whatever: "dingo" }, b: 2, c: 3 };a._$shallow( { a: { larb: "foo" }, z:26 } );
// a now looks like: {a: { whatever: "dingo", larb: "foo" }, b: 2, c:3, z: 26}
Object.prototype._$get( path ) Traverses nested objects to return the value specified
by the path. If an element in the path doesn't exist, an empty object is used.
var a = { one: 1, two: { a: 2, b: 3 } };
console.log( a._$get( "one" ) ); // prints 1
console.log( a._$get( "two/a" ) ); // prints 2
console.log( a._$get( "three" ) ); // prints undefined
console.log( a._$get( "three/alpha" ) ); // prints undefinedObject.prototype._$put( path, value ) Traverses nested objects using the specified path,
setting the value given to the last element in the list.
var a = { one: 1, two: { a: 2, b: 3 } };
a._$put( "three/alpha", "dingo" );
console.log( a ); // prints out { one: 1, two: { a: 2, b: 3 }, three: { alpha: "dingo" } }Callback Support
----------------
Number.prototype._$counter Returns a function that calls another function after N invocations.
This is useful when you want to process all items in a list with async calls, and then do something
else after they've all completed.
function _process( item, callback ) {
setTimeout( function () {
console.log( "processed: " + item );
_$g( callback ) ();
}, Math.random() * 10000 );
}var a = [ 'one', 'two', 'three', 'four', 'five', 'six' ];
var begin = Date.now(), end;
var _post = a.length._$counter( function() {
end = Date.now();
console.log( "finished processing items at " + (new Date(end)).toString() );
console.log( "took " + ( end - begin ) + " milliseconds." );
} );
console.log( "starting processing items at " + (new Date(begin)).toString() );
a._$each( function( e ) { _process( e, _post ); } );
Array.prototype._$sim( each_function, after_function ) Calls the first function on each
element in an array. After each function has called back, it calls the second function.
function _process( element, index, callback ) {
setTimeout( function () {
console.log( "processed: " + element + "(" + index + ")" );
_$g( callback ) ();
}, Math.random() * 10000 );
}var a = [ 'zero', 'one', 'two', 'three', 'four', 'five', 'six' ];
var begin = Date.now(), end;
a._$sim( _process, function () {
end = Date.now();
console.log( "finished processing items at " + (new Date(end)).toString() );
console.log( "took " + ( end - begin ) + " milliseconds." );
} );
Function.prototype._$capture Returns a version of a function that captures exceptions,
returning them as the first parameter to an error handling callback. This is useful if you're
using a function or library that throws exceptions, but you want to use it in an async
manner.
For example, consider the situation in node where you need to parse a JSON file read with
the async fs.readFile() function. The _$capture function lets you separate the logic for
processing processing the file from the logic of handling exceptions so the caller can
decide what action to take when there's an exception.
var fs = require( 'fs' );
var filepath = process.argv[2];console.log( "processing file " + filepath );
fs.readFile( filepath, function( err, data ) {
if( err ) { return console.log( "error reading file " + filepath + ": " + err.toString() ); }
var parsed = JSON.parse( data.toString() );
console.log( "oh look! parsed data:" );
console.log( parsed );
// continue processing
}._$capture( function( err ) {
console.log( "error processing file " + filepath + ": " + err.toString() );
// do whatever you need to do to recover from an error in the JSON file
} ) );
Cross-Environment Support
-------------------------
Object.prototype._$keys() It turns out that some JavaScript environments
don't provide Object.keys(). Who knew? This function is punched into Object's
prototype and calls Object.keys() if it's available or calls a local
implementation.
// this will return the array [ 'a', 'b', 'c' ]
{ a: "1", b: "2", c: "3" }._$keys();
_$construct( name, defaults, global ) constructs a constructor that takes an
object containing properties to initialize object instances with. If items in
the defaults objects are specified, they'll be copied from the defaults.
If you define the functions _$init or _$initAsync in the constructor's prototype,
the _$construct() constructor will call them immediately or via _$nextTick.
_$construct( 'SampleObject', { location: [ 0, 0 ], log: '/var/log/sample.log' } );SampleObject.prototype._$init = function() {
console.log( "SampleObject's _$init function" );
};
SampleObject.prototype._$initAsync = function( callback ) {
callback = callback?callback:_$f;
console.log( "SampleObject's _$initAsync function" );
callback.call( this );
};
// This will create an object with the properties { location: [ 0, 0 ], log: '/var/log/sample.log' }
var foo = new SampleObject();
// This will create an object with properties { location: [ 0, 0 ], log: '/var/log/sample.log', title: 'whatever' }
var bar = new SampleObject( { title: 'whatever' } );
// This will create an object with properties { location: [ -10, 12 ], log: '/var/log/sample.log' }
var baz = new SampleObject( { location: [-10, 12] } );
A pattern I use frequently in node.js modules is something like this:
( function () {
require( 'sn-core' ); _$construct( 'exports', { / instance defaults go here / }, module };
module.exports.prototype._$init = function () {
// This function is called after defaults are applied.
};
} ) ();
And I would use the module like any other:
var example = require( 'example' );var whatever = new example( {a: 1, b:2} );
Function.prototype._$nextTick() Calls the recieving function after after the current run through the event loop.
var items = [];function _log_this( param, value ) {
console.log( "logging: " + ((param)?param.toString():"undefined") );
items.push( value );
}_log_this( "line 1", 33 );
_log_this._$nextTick( "line 3", 77 );
_log_this( "line 2", 44 );// items will now contain [33, 44, 77]. 77 is at the end because _log_this( "line 3", 77 )
// is executed after _log_this( "line 2", 44 )