sn-core

1.0.0 • Public • Published

sn-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 7

Function.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 18

Function.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.5

Function.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 false

Array.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 undefined

Object.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 )

Readme

Keywords

Package Sidebar

Install

npm i sn-core

Weekly Downloads

0

Version

1.0.0

License

none

Last publish

Collaborators

  • meadhbh