Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

function-composer

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

function-composer - npm Package Compare versions

Comparing version 0.1.0 to 0.2.0

examples/browser_amd.html

180

benchmark/benchmark.js

@@ -1,139 +0,91 @@

var compose = require('./../index');
var type = require('./../lib/types').type;
var compose = require('../function-composer');
var I_MAX = 1e7;
var I_MAX = 1e6;
var count = 0;
function f() {
return count++;
}
var fns = {
'number': f,
'number,boolean': f,
'number,number': f,
'number,date': f,
'string': f,
'string,boolean': f
};
var fn1 = compose(fns);
function benchmark(name, test) {
var start = +new Date();
var repetitions = test();
var end = +new Date();
var duration = end - start;
function fOptimal1(arg0, arg1) {
switch(type(arg0)) {
case 'number':
switch(type(arg1)) {
case 'boolean': return count++;
case 'number': return count++;
case 'date': return count++;
default: return count++;
}
console.log(name + ': ' + repetitions.toExponential() + ' calls, ' +
Math.round(duration) + ' ms, ' +
parseFloat((duration / repetitions).toPrecision(4)).toExponential() + ' ms per call');
case 'string':
switch(type(arg1)) {
case 'boolean': return count++;
default: return count++;
}
}
return {
repetitions: repetitions,
duration: duration
};
}
// interesting: not using local variables and no calling of type() seems to be fastest
function fOptimal2(arg0, arg1) {
switch(typeof(arg0)) {
case 'number':
if (typeof arg1 === 'boolean') {
return count++;
}
if (typeof arg1 === 'number') {
return count++;
}
//var count = 0;
//function direct() {
// var args = Array.prototype.slice.apply(arguments);
// args.forEach(function (arg) {
// count = count + (arg && arg.length) ? arg.length : 1;
// });
// return count;
//}
if (arg1 instanceof Date) {
return count++;
}
return count++;
case 'string':
switch(typeof(arg1)) {
case 'boolean': return count++;
default: return count++;
}
function direct(text) {
var reverse = '';
var i = text.length;
while (i > 0) {
reverse += text.substring(i-1, i);
i--;
}
return reverse;
}
// No worry about inlining the function, it's not faster than calling the function
// Using if instead of switch is way faster though!
// For some weird reason this is even faster than none. How's that possible?
function fOptimal3(arg0, arg1) {
if (typeof arg0 === 'number') {
if (typeof arg1 === 'boolean') {
return f(arg0, arg1);
}
//var count = 0;
//function direct() {
// return count++;
//}
if (typeof arg1 === 'number') {
return f(arg0, arg1);
}
var composed = compose({
'number': direct,
'number,boolean': direct,
'number,number': direct,
'number,date': direct,
'string': direct,
'string,boolean': direct
});
if (arg1 instanceof Date) {
return f(arg0, arg1);
}
return f(arg0);
//console.log(composed.toString())
var directResult = benchmark('Direct', function () {
var i, r, d = new Date();
for (i = 0; i < I_MAX; i++) {
r = direct(1, d);
r = direct('hello you there', false);
r = direct(2, 4);
}
if (typeof arg0 === 'string') {
if (typeof arg1 === 'boolean') {
return f(arg0, arg1);
}
return I_MAX * 3;
});
return f(arg0);
var composedResult = benchmark('Composed', function () {
var i, r, d = new Date();
for (i = 0; i < I_MAX; i++) {
r = composed(1, d);
r = composed('hello you there', false);
r = composed(2, 4);
}
}
return I_MAX * 3;
});
var i, r, d = new Date();
console.time('none');
for (i = 0; i < I_MAX; i++) {
r = f(1, d);
r = f('hi', false);
r = f(2, 4);
}
console.timeEnd('none');
var overhead = ((composedResult.duration - directResult.duration) / composedResult.repetitions);
var percentage = overhead / (directResult.duration / directResult.repetitions) * 100;
console.log('Overhead: ' + percentage.toPrecision(4) + '%, ' +
parseFloat(overhead.toPrecision(4)).toExponential() + ' ms per call');
// Output is for example:
// Direct: 3e+6 calls, 1865 ms, 6.217e-4 ms per call
// Composed: 3e+6 calls, 1982 ms, 6.607e-4 ms per call
// Overhead: 6.3%, 3.9e-5 ms per call
console.time('optimal1');
for (i = 0; i < I_MAX; i++) {
r = fOptimal1(1, d);
r = fOptimal1('hi', false);
r = fOptimal1(2, 4);
}
console.timeEnd('optimal1');
console.time('optimal2');
for (i = 0; i < I_MAX; i++) {
r = fOptimal2(1, d);
r = fOptimal2('hi', false);
r = fOptimal2(2, 4);
}
console.timeEnd('optimal2');
console.time('optimal3');
for (i = 0; i < I_MAX; i++) {
r = fOptimal3(1, d);
r = fOptimal3('hi', false);
r = fOptimal3(2, 4);
}
console.timeEnd('optimal3');
console.time('switch');
for (i = 0; i < I_MAX; i++) {
r = fn1(1, d);
r = fn1('hi', false);
r = fn1(2, 4);
}
console.timeEnd('switch');

@@ -1,2 +0,2 @@

var compose = require('../index');
var compose = require('../function-composer');

@@ -3,0 +3,0 @@ // compose a new function

# History
## 2014-10-23, version 0.2.0
- Implemented support for named functions.
- Implemented support for type conversions.
- Implemented support for custom types.
- Library packaged as UMD, usable with CommonJS (node.js), AMD, and browser globals.
## 2014-10-21, version 0.1.0

@@ -5,0 +13,0 @@

{
"name": "function-composer",
"version": "0.1.0",
"version": "0.2.0",
"description": "Compose functions with multiple type signatures",

@@ -14,4 +14,5 @@ "author": "Jos de Jong <wjosdejong@gmail.com> (https://github.com/josdejong)",

"function",
"arguments",
"compose",
"signature"
"types"
],

@@ -21,6 +22,8 @@ "dependencies": {},

"istanbul": "^0.3.2",
"mocha": "^1.21.5"
"mocha": "^1.21.5",
"uglify-js": "^2.4.15"
},
"main": "./index",
"main": "./function-composer",
"scripts": {
"minify": "uglifyjs function-composer.js -o function-composer.min.js",
"test": "mocha test --recursive",

@@ -27,0 +30,0 @@ "coverage": "istanbul cover _mocha -- test --recursive; echo \"\nCoverage report is available at ./coverage/lcov-report/index.html\""

@@ -6,2 +6,8 @@ function-composer

Features:
- Compose multiple functions with different signatures into one.
- Type-checking of input arguments.
- Automatic type conversion of arguments.
Supported environments: node.js, Chrome, Firefox, Safari, Opera, IE9+.

@@ -50,12 +56,73 @@

# Performance
Type checking input arguments adds some overhead to a function. For very small
functions this overhead can be larger than the function execution itself is,
but for any non-trivial function the overhead is typically small to neglectable.
You need to keep in mind though that you probably would have to do the type
checking done by `function-composer` anyway.
# API
## Construction
```js
compose(signatures: Object.<string, function>) : function
compose(name: string, signatures: Object.<string, function>) : function
```
## Properties
- `compose.tests: Object`
A map with type checking tests. Add custom types like:
```js
function Person(...) {
...
}
compose.tests['Person'] = function (x) {
return x instanceof Person;
};
```
- `compose.conversions: Array`
An Array with built-in conversions. Empty by default. Can be used for example
to defined conversions from `boolean` to `number`. For example:
```js
compose.conversions.push({
from: 'boolean',
to: 'number',
convert: function (x) {
return +x;
});
```
# Roadmap
- Create named functions.
- Add new types (already possible but not yet documented)
- Automatic casting, for example from boolean to number.
## Version 1
- Extend function signatures:
- Any type arguments like `*, boolean`.
- Ellipsis like `string, ...`.
- Optional arguments like `number?, array`.
- Any type arguments like `'*, boolean'`
- Ellipsis like `'string, ...'`
- Optional arguments like `'number?, array'`
- Multiple types per argument like `number | string, number'`
- Create a good benchmark, to get insight in the overhead.
- Add a bundle for use in the browser.
## Version 2
- Extend function signatures:
- Constants like `'"linear" | "cubic"'`.
- Object definitions like `'{name: string, age: number}'`
- Object definitions like `'Object.<string, Person>'`
- Array definitions like `'Array.<Person>'`
# Minify
To generate the minified version of the library, run:
npm run minify
// test parse
var assert = require('assert');
var compose = require('../index');
var compose = require('../function-composer');
describe('parse', function() {
it('should compose an empty function', function() {
var fn = compose({});
assert.throws(function () {
fn();
}, /TypeError: Wrong function signature/);
assert(fn.signatures instanceof Object);
assert.deepEqual(fn.signatures, []);
});
it('should compose a function with zero arguments', function() {

@@ -21,2 +30,13 @@ var fns = {

it('should create a named function', function() {
var fn = compose('myFunction', {
'': function () {
return 'noargs';
}
});
assert.equal(fn(), 'noargs');
assert.equal(fn.name, 'myFunction');
});
it('should compose a function with one argument', function() {

@@ -37,3 +57,3 @@ var fns = {

assert.equal(fn(2), 'number:2');
assert.equal(fn('hi'), 'string:hi');
assert.equal(fn('foo'), 'string:foo');
assert.equal(fn(false), 'boolean:false');

@@ -62,3 +82,3 @@ assert(fn.signatures instanceof Object);

assert.equal(fn(2), 'number:2');
assert.equal(fn('hi'), 'string:hi');
assert.equal(fn('foo'), 'string:foo');
assert.equal(fn(2, false), 'number,boolean:2,false');

@@ -72,2 +92,19 @@ assert(fn.signatures instanceof Object);

it('should correctly recognize Date from Object (both are an Object)', function() {
var fns = {
'Object': function (value) {
assert(value instanceof Object);
return 'Object';
},
'Date': function (value) {
assert(value instanceof Date);
return 'Date';
}
};
var fn = compose(fns);
assert.equal(fn({foo: 'bar'}), 'Object');
assert.equal(fn(new Date()), 'Date');
});
it('should throw an error when providing an unsupported type of argument', function() {

@@ -90,7 +127,107 @@ var fn = compose({

assert.throws(function () {fn(1, 2)}, /TypeError: Wrong number of arguments/); // TODO: should be changed into an ArgumentsError?
assert.throws(function () {fn(1, 2)}, /TypeError: Wrong number of arguments/);
});
it('should throw an error when composing with an unknown type', function() {
assert.throws(function () {
var fn = compose({
'foo': function (value) {
return 'number:' + value;
}
});
}, /Error: Unknown type "foo"/);
});
// TODO: test compose.types
it('should give a hint when composing with a wrongly cased type', function() {
assert.throws(function () {
var fn = compose({
'array': function (value) {
return 'array:' + value;
}
});
}, /Error: Unknown type "array". Did you mean "Array"?/);
assert.throws(function () {
var fn = compose({
'Function': function (value) {
return 'Function:' + value;
}
});
}, /Error: Unknown type "Function". Did you mean "function"?/);
});
describe('conversions' , function () {
before(function () {
compose.conversions = [
{from: 'boolean', to: 'number', convert: function (x) {return +x;}},
{from: 'boolean', to: 'string', convert: function (x) {return x + '';}},
{from: 'number', to: 'string', convert: function (x) {return x + '';}}
];
});
after(function () {
compose.conversions = [];
});
it('should add conversions to a function with one argument', function() {
var fn = compose({
'string': function (a) {
return a;
}
});
assert.equal(fn(2), '2');
assert.equal(fn(false), 'false');
assert.equal(fn('foo'), 'foo');
});
it('should add conversions to a function with multiple arguments', function() {
// note: we add 'string, string' first, and `string, number` afterwards,
// to test whether the conversions are correctly ordered.
var fn = compose({
'string, string': function (a, b) {
assert.equal(typeof a, 'string');
assert.equal(typeof b, 'string');
return 'string, string';
},
'string, number': function (a, b) {
assert.equal(typeof a, 'string');
assert.equal(typeof b, 'number');
return 'string, number';
}
});
assert.equal(fn(true, false), 'string, number');
assert.equal(fn(true, 2), 'string, number');
assert.equal(fn(true, 'foo'), 'string, string');
assert.equal(fn(2, false), 'string, number');
assert.equal(fn(2, 3), 'string, number');
assert.equal(fn(2, 'foo'), 'string, string');
assert.equal(fn('foo', true), 'string, number');
assert.equal(fn('foo', 2), 'string, number');
assert.equal(fn('foo', 'foo'), 'string, string');
});
it('should add non-conflicting conversions to a function with one argument', function() {
var fn = compose({
'number': function (a) {
return a;
},
'string': function (a) {
return a;
}
});
// booleans should be converted to number
assert.equal(fn(false), 0);
assert.equal(fn(true), 1);
// numbers and strings should be left as is
assert.equal(fn(2), 2);
assert.equal(fn('foo'), 'foo');
});
});
// TODO: test compose.tests
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc