Comparing version 1.1.3 to 1.2.0
var functools = (function(undefined){ | ||
function isArray(el){ | ||
return Object.prototype.toString.call(el) == '[object Array]'; | ||
} | ||
/** | ||
@@ -22,3 +26,4 @@ * Function composition implementation | ||
if(error || fns.length<=i){ | ||
return callback(error, value); | ||
callback(error, value); | ||
return; | ||
} | ||
@@ -36,5 +41,10 @@ | ||
* a list containing the result of applying each fn to the arguments. | ||
* | ||
* usage examples; | ||
* juxt(Number, String, Array)(true) => [1, "true", [true]] | ||
* juxt({ "num": Number, "str": String, "arr": Array })(true) => { "num": 1, "str": "true", "arr": [true] } | ||
*/ | ||
function juxt(/* functions */){ | ||
var fns = arguments; | ||
var fns = arguments.length > 1 || typeof arguments[0] != 'object' ? arguments : arguments[0]; | ||
return function(){ | ||
@@ -44,10 +54,11 @@ var args = arguments; | ||
return fn.apply(undefined, args); | ||
}, fns); | ||
}, fns); | ||
}; | ||
} | ||
juxt.async = function(/* functions */){ | ||
var fns = arguments; | ||
return function(/* args, */callback){ | ||
var args = Array.prototype.slice.call(arguments, 0, arguments.length-1), | ||
var fns = arguments.length > 1 || typeof arguments[0] != 'object' ? arguments : arguments[0]; | ||
return function(/* args, callback */){ | ||
var args = Array.prototype.slice.call(arguments, 0, arguments.length-1), | ||
callback = arguments[arguments.length-1]; | ||
@@ -102,3 +113,4 @@ | ||
if(i>=iterable.length){ | ||
return callback(accumulation); | ||
callback(accumulation); | ||
return; | ||
} | ||
@@ -109,3 +121,3 @@ | ||
})(0); | ||
}; | ||
} | ||
@@ -115,24 +127,51 @@ /** | ||
*/ | ||
function map(fn,iterable){ | ||
var seq = Array.prototype.slice.call(iterable); | ||
for(var i = -1, len=seq.length; ++i < len; ){ | ||
seq[i] = fn(seq[i],i,seq); | ||
}; | ||
function map(fn, iterable){ | ||
var clone, i, len, key; | ||
return seq; | ||
if(isArray(iterable)){ | ||
clone = Array.prototype.slice.call(iterable, 0); | ||
i = -1; | ||
len = clone.length; | ||
while(++i<len){ | ||
clone[i] = fn(clone[i], i, clone); | ||
} | ||
} else if(typeof iterable == 'object') { | ||
clone = {}; | ||
for(key in iterable){ | ||
clone[key] = fn(iterable[key], key, clone); | ||
} | ||
} | ||
return clone; | ||
} | ||
map.async = function map_async(fn, iterable, callback){ | ||
var seq = Array.prototype.slice.call(iterable); | ||
(function(i, error, rpl){ | ||
var clone, iter, i, len, key, value, | ||
list = isArray(iterable); | ||
iter = list ? Array.prototype.slice.call(iterable) : Object.keys(iterable); | ||
clone = list ? iter : {}; | ||
(function next(i, error, rpl){ | ||
rpl && ( seq[i-1] = rpl ); | ||
key = list ? i-1 : iter[i-1]; | ||
value = list ? clone[i] : iterable[ iter[i] ]; | ||
if(error || i>=seq.length){ | ||
return callback(error, seq); | ||
arguments.length > 2 && ( clone[key] = rpl ); | ||
if(error || i>=iter.length){ | ||
callback(error, clone); | ||
return; | ||
} | ||
fn(seq[i], partial(arguments.callee, [i+1])); | ||
fn(value, next.bind(undefined, i+1)); | ||
})(0); | ||
}; | ||
@@ -169,3 +208,4 @@ | ||
if(error || i>=iterable.length){ | ||
return callback(error, accumulator); | ||
callback(error, accumulator); | ||
return; | ||
} | ||
@@ -172,0 +212,0 @@ |
{ | ||
"name":"functools", | ||
"version":"1.1.3", | ||
"version":"1.2.0", | ||
"description":"A minimal library of functional operations", | ||
@@ -5,0 +5,0 @@ "author":"Azer Koculu <azer@kodfabrik.com>", |
216
README.md
functools is a JavaScript library for functional programming. | ||
Inspired languages: Common Lisp, Clojure and Python. | ||
Version: 1.1.2 | ||
Inspired by: Common Lisp, Clojure and Python. | ||
## SYNOPSIS | ||
# SYNOPSIS | ||
A typical usage: | ||
```javascript | ||
var map = require("functools").map; | ||
var seq = [3,1,4,1,5,9]; | ||
functools.map(function(el,ind,seq){ | ||
return el+ind; | ||
},seq); | ||
``` | ||
Function Composition: | ||
```javascript | ||
var compose = require("functools").compose; | ||
var compose = require("functools").compose; | ||
compose(select, update, prettify, display)("body .messages"); | ||
compose(select, update, prettify, display)("body .messages"); | ||
``` | ||
@@ -41,24 +28,44 @@ | ||
Async Juxtaposition: | ||
```javascript | ||
function turkish(word, callback){ /* some magic here */ } | ||
function french(word, callback){ /* some magic here */ } | ||
function polish(word, callback){ /* some magic here */ } | ||
juxt.async({ 'tr': turkish, 'fr': french, 'pl': polish })("hello", function(error, results){ | ||
assert.equal(results.tr, "merhaba"); | ||
assert.equal(results.fr, "bonjour"); | ||
assert.equal(results.pl, "cześć"); | ||
}); | ||
``` | ||
Currying: | ||
```javascript | ||
var fn = require("functools"); | ||
var fn = require("functools"); | ||
var pickEvens = fn.curry(fn.filter)(function(num){ return num%2==0 }); | ||
var pickEvens = fn.curry(fn.filter)(function(num){ return num%2==0 }); | ||
pickEvens([3,1,4]) // returns [4] | ||
pickEvens([1,5,9,2,6,5]) // returns [2,6] | ||
pickEvens([3,1,4]) // returns [4] | ||
pickEvens([1,5,9,2,6,5]) // returns [2,6] | ||
``` | ||
## DESCRIPTION | ||
# INSTALL | ||
functools is a library for functional programming written in JavaScript. It's | ||
based on a single CommonJS module (that can be run on web browsers, as well) that consists of several function manipulation and list | ||
iteration tools. | ||
```bash | ||
$ npm install functools | ||
``` | ||
## API | ||
or | ||
### compose(*functions ...*)(*value*) | ||
```bash | ||
$ wget https://raw.github.com/azer/functools/master/lib/functools.js | ||
``` | ||
# API | ||
## compose(*functions ...*)(*value*) | ||
Combine *functions* in a new one, passing the result of each function to next | ||
@@ -68,8 +75,8 @@ one, from left to right. | ||
```javascript | ||
function cube(x){ return x*x*x }; | ||
function cube(x){ return x*x*x }; | ||
compose(Math.sqrt,cube)(4); // returns 8 | ||
compose(Math.sqrt,cube)(4); // returns 8 | ||
``` | ||
### compose.async(*functions ...*)(*value*,*callback*) | ||
## compose.async(*functions ...*)(*value*,*callback*) | ||
@@ -81,15 +88,15 @@ Asynchronous, continuation passing based version of compose function. Requires | ||
```javascript | ||
function receiveMessage(url, callback){ ... callback(); } | ||
function findRelatedUser(user, message, callback){ ... callback(null, user, message); } | ||
function transmitMessage(user, message){ ... callback(); } | ||
function receiveMessage(message, callback){ ... callback(); } | ||
function findRelatedUser(message, callback){ ... callback(null, user, message); } | ||
function transmitMessage(user, message){ ... callback(); } | ||
var messageTransmission = compose.async(receiveMessage, findRelatedUser, transmitMessage); | ||
var messageTransmission = compose.async(receiveMessage, findRelatedUser, transmitMessage); | ||
messageTransmission({ msg:"Hello !", 'user': 3 }, function(error, result){ | ||
... | ||
}) | ||
messageTransmission({ msg:"Hello !", 'user': 3 }, function(error, result){ | ||
... | ||
}) | ||
``` | ||
### curry(*function*, *args ...*) | ||
## curry(*function*, *args ...*) | ||
@@ -99,12 +106,12 @@ Transform multiple-argument *function* into a chain of functions that return each other until all arguments are gathered. | ||
```javascript | ||
function sum(x,y){ return x+y; } | ||
function sum(x,y){ return x+y; } | ||
var add3 = curry(sum, 3); | ||
var add3 = curry(sum, 3); | ||
add3(14); // returns 17 | ||
add3(20); // returns 23 | ||
add3(14); // returns 17 | ||
add3(20); // returns 23 | ||
``` | ||
### partial(*function*,*initial arguments*,*context *) | ||
## partial(*function*,*initial arguments*,*context *) | ||
@@ -114,38 +121,54 @@ Return a new function which will call *function* with the gathered arguments. | ||
```javascript | ||
function testPartial(){ | ||
var args = reduce(function(x,y){ x+", "+y },arguments); | ||
function testPartial(){ | ||
var args = reduce(function(x,y){ x+", "+y },arguments); | ||
console.log("this:",this); | ||
console.log("args:",args); | ||
} | ||
console.log("this:",this); | ||
console.log("args:",args); | ||
} | ||
partial(testPartial, [3,14], 3.14159)(1,5,9); | ||
partial(testPartial, [3,14], 3.14159)(1,5,9); | ||
``` | ||
The example code above will output: | ||
``` | ||
this: 3.14159 | ||
args: 3,14,1,5,9 | ||
``` | ||
this: 3.14159 | ||
args: 3,14,1,5,9 | ||
## each(*function*,*iterable*) | ||
### each(*function*,*iterable*) | ||
Call *function* once for element in *iterable*. | ||
```javascript | ||
each(function(el,ind,list){ console.assert( el == list[ind] ); }, [3,1,4]); | ||
each(function(el,ind,list){ console.assert( el == list[ind] ); }, [3,1,4]); | ||
``` | ||
### map(*function*,*iterable*) | ||
## map(*function*,*iterable*) | ||
Invoke *function* once for each element of *iterable*. Creates a new array | ||
Invoke *function* once for each element of *iterable*. Creates a new iterable | ||
containing the values returned by the function. | ||
```javascript | ||
map(function(el,ind,list){ return el*el },[3,1,4,1,5,9]); // returns [9,1,16,1,25,81] | ||
function square(n){ | ||
return n*n; | ||
} | ||
map(square,[3,1,4,1,5,9]); // returns [9,1,16,1,25,81] | ||
``` | ||
Objects can be passed as well; | ||
### map.async(*function*,*iterable*, *callback*) | ||
```javascript | ||
var dict = { 'en':'hello', 'tr': 'merhaba', 'fr':'bonjour' }; | ||
function capitalize(){ | ||
return string.charAt(0).toUpperCase() + string.slice(1); | ||
} | ||
map(capitalize, dict); // returns { 'en':'Hello', 'tr':'Merhaba', 'fr':'Bonjour' } | ||
``` | ||
## map.async(*function*,*iterable*, *callback*) | ||
Apply async *function* to every item of *iterable*, receiving a callback | ||
@@ -155,20 +178,21 @@ function which takes error (if there is) and replacement parameters. | ||
```javascript | ||
function readFile(id, callback){ ... callback(undefined, data); } | ||
function readFile(id, callback){ ... callback(undefined, data); } | ||
map.async(readFile, ['./foo/bar', './foo/qux', './corge'], function(error, files){ | ||
if(error) throw error; | ||
map.async(readFile, ['./foo/bar', './foo/qux', './corge'], function(error, files){ | ||
if(error) throw error; | ||
console.log(files[0]); // will put the content of ./foo/bar | ||
}); | ||
console.log(files[0]); // will put the content of ./foo/bar | ||
}); | ||
``` | ||
### filter(*function*,*iterable*) | ||
## filter(*function*,*iterable*) | ||
Construct a new array from those elements of *iterable* for which *function* returns true. | ||
```javascript | ||
filter(function(el,ind,list){ return el%2==0 },[3,1,4]); // returns [4] | ||
``` | ||
filter(function(el,ind,list){ return el%2==0 },[3,1,4]); // returns [4] | ||
## filter.async(*function*,*iterable*, *callback*) | ||
### filter.async(*function*,*iterable*, *callback*) | ||
Call async *function* once for each element in *iterable*, receiving a boolean | ||
@@ -180,9 +204,9 @@ parameter, and construct a new array of all the values for which *function* | ||
var users = [ 3, 5, 8, 13, 21 ]; // only user#3 and user#8 have permission in this example | ||
var users = [ 3, 5, 8, 13, 21 ]; // only user#3 and user#8 have permission in this example | ||
function hasPermission(userId, callback){ ... callback(/* true or false *); } | ||
function hasPermission(userId, callback){ ... callback(/* true or false */); } | ||
filter.async(hasPermission, users, function(permittedUsers){ | ||
assert.equal(permittedUsers.length, 4); | ||
}); | ||
filter.async(hasPermission, users, function(permittedUsers){ | ||
assert.equal(permittedUsers.length, 4); | ||
}); | ||
@@ -200,11 +224,9 @@ ``` | ||
```javascript | ||
function inc1(n){ return n+1 }; | ||
function inc2(n){ return n+2 }; | ||
function inc3(n){ return n+3 }; | ||
function inc1(n){ return n+1 }; | ||
function inc2(n){ return n+2 }; | ||
function inc3(n){ return n+3 }; | ||
juxt(inc1, inc2, inc3)([3,1,4]); // returns [4,3,7] | ||
juxt(inc1, inc2, inc3)(314); // returns [315,316,317] | ||
``` | ||
## juxt.async(*functions ...*) | ||
@@ -215,14 +237,14 @@ | ||
```javascript | ||
function md5(path, callback){ fetch(path, callback); } | ||
function sha1(path, callback){ fetch(path, callback); } | ||
function crc32(path, callback){ fetch(path, callback); } | ||
function turkish(word, callback){ /* some magic here */ } | ||
function french(word, callback){ /* some magic here */ } | ||
function polish(word, callback){ /* some magic here */ } | ||
juxt.async(md5, sha1, crc32)("hello world", function(error, result){ | ||
result[0] => md5("hello world") | ||
result[1] => sha1("hello world") | ||
result[2] => crc32("hello world") | ||
}); | ||
juxt.async(turkish, french, polish)("hello", function(error, results){ | ||
assert.equal(results[0], "merhaba"); | ||
assert.equal(results[1], "bonjour"); | ||
assert.equal(results[2], "cześć"); | ||
}); | ||
``` | ||
### reduce(*function*,*iterable*) | ||
## reduce(*function*,*iterable*) | ||
@@ -233,6 +255,6 @@ Apply *function* cumulatively to the items of *iterable*, as to reduce the | ||
```javascript | ||
reduce(function(x,y){ return x*y }, [3,1,4]); // returns 12 | ||
reduce(function(x,y){ return x*y }, [3,1,4]); // returns 12 | ||
``` | ||
### reduce.async(*function*,*iterable*, *callback*) | ||
## reduce.async(*function*,*iterable*, *callback*) | ||
@@ -243,15 +265,15 @@ Async implementation of *reduce*. | ||
var users = [2, 3, 5, 8, 13]; | ||
var users = [2, 3, 5, 8, 13]; | ||
function usernames(accum, userId){ ... callback(undefined, accum + ', ' + username); } | ||
function usernames(accum, userId){ ... callback(undefined, accum + ', ' + username); } | ||
reduce.async(usernames, users, function(error, result){ | ||
if(error) throw error; | ||
reduce.async(usernames, users, function(error, result){ | ||
if(error) throw error; | ||
console.log(result); // foo, bar, qux ... | ||
}); | ||
console.log(result); // foo, bar, qux ... | ||
}); | ||
``` | ||
## SEE ALSO | ||
# SEE ALSO | ||
- [Functional Programming - Eloquent JavaScript](http://eloquentjavascript.net/chapter6.html) |
@@ -62,9 +62,9 @@ if(typeof require!='undefined'){ | ||
function sum(a,b,c){ | ||
return a+b+c; | ||
} | ||
return a+b+c; | ||
} | ||
assert.equal( functools.curry(sum)()(3)()(1)(4),8); | ||
assert.equal( functools.curry(sum,3,1,4),8); | ||
assert.equal( functools.curry(sum,3)(1,4),8); | ||
assert.equal( functools.curry(sum)()(3)()(1)(4),8); | ||
assert.equal( functools.curry(sum,3,1,4),8); | ||
assert.equal( functools.curry(sum,3)(1,4),8); | ||
} | ||
@@ -138,2 +138,9 @@ | ||
assert.equal(result[3], 6); | ||
result = functools.juxt({ 'cube': cube, 'sum': sum, 'div': div, 'mul': mul })(2); | ||
assert.equal(result.cube, 8); | ||
assert.equal(result.sum, 4); | ||
assert.equal(result.div, 1); | ||
assert.equal(result.mul, 6); | ||
} | ||
@@ -162,7 +169,17 @@ | ||
functools.juxt.async(cube, sum, div, mul)(2,function(error, result){ | ||
assert.equal(result[0], 8); | ||
assert.equal(result[1], 4); | ||
assert.equal(result[2], 1); | ||
assert.equal(result[3], 6); | ||
if(error) throw error; | ||
assert.equal(result[0], 8); | ||
assert.equal(result[1], 4); | ||
assert.equal(result[2], 1); | ||
assert.equal(result[3], 6); | ||
}); | ||
functools.juxt.async({ 'cube':cube, 'sum':sum, 'div':div, 'mul':mul })(2,function(error, result){ | ||
if(error) throw error; | ||
assert.equal(result.cube, 8); | ||
assert.equal(result.sum, 4); | ||
assert.equal(result.div, 1); | ||
assert.equal(result.mul, 6); | ||
}); | ||
} | ||
@@ -195,2 +212,18 @@ | ||
assert.equal(seq[2], 16); | ||
range = { 'a': 3, 'b': 1, 'c': 4 }; | ||
seq = functools.map(function(el, key, self){ | ||
assert.equal(el, range[key]); | ||
return el * el; | ||
}, range); | ||
assert.equal(range.a, 3); | ||
assert.equal(range.b, 1); | ||
assert.equal(range.c, 4); | ||
assert.equal(seq.a, 9); | ||
assert.equal(seq.b, 1); | ||
assert.equal(seq.c, 16); | ||
}; | ||
@@ -210,3 +243,2 @@ | ||
assert.equal(range[0], 3); | ||
@@ -220,2 +252,20 @@ assert.equal(range[1], 1); | ||
}); | ||
range = { 'a': 3, 'b': 1, 'c': 4 }; | ||
functools.map.async(function(el,callback){ callback(undefined, el*el); }, range, function(error, seq){ | ||
if(error){ | ||
throw error; | ||
} | ||
assert.equal(range.a, 3); | ||
assert.equal(range.b, 1); | ||
assert.equal(range.c, 4); | ||
assert.equal(seq.a, 9); | ||
assert.equal(seq.b, 1); | ||
assert.equal(seq.c, 16); | ||
}); | ||
}; | ||
@@ -222,0 +272,0 @@ |
50501
457
269