Comparing version 0.0.1 to 0.0.2
@@ -1,19 +0,4 @@ | ||
var http = require('http'), | ||
Promise = require('../lib/pacta').Promise; | ||
var http = require('./promised-http'), | ||
getJSON = http.getJSON; | ||
var promisedJSON = function (url) { | ||
var promise = new Promise(); | ||
http.get(url, function (res) { | ||
var body = ''; | ||
res.on('data', function (chunk) { | ||
body += chunk; | ||
}); | ||
res.on('end', function () { | ||
promise.resolve(JSON.parse(body)); | ||
}); | ||
}); | ||
return promise; | ||
}; | ||
var random = function (coll) { | ||
@@ -23,8 +8,8 @@ return coll[Math.floor(Math.random() * coll.length)]; | ||
var promisedPrefixes = promisedJSON('http://codenames.clivemurray.com/data/prefixes.json'), | ||
promisedAnimals = promisedJSON('http://codenames.clivemurray.com/data/animals.json'), | ||
prefixesAndAnimals = promisedPrefixes.concat(promisedAnimals); | ||
var promisedPrefixes = getJSON('http://codenames.clivemurray.com/data/prefixes.json'), | ||
promisedAnimals = getJSON('http://codenames.clivemurray.com/data/animals.json'), | ||
prefixesAndAnimals = promisedPrefixes.combine(promisedAnimals); | ||
var promisedCodeName = function () { | ||
return prefixesAndAnimals.map(function (prefixes, animals) { | ||
return prefixesAndAnimals.explode(function (prefixes, animals) { | ||
var prefix = random(prefixes), | ||
@@ -35,3 +20,3 @@ animal = random(animals); | ||
}); | ||
} | ||
}; | ||
@@ -38,0 +23,0 @@ promisedCodeName().map(console.log); |
@@ -7,3 +7,3 @@ var events = require('events'); | ||
if (typeof value !== 'undefined') { | ||
if (arguments.length) { | ||
this.resolve(value); | ||
@@ -13,3 +13,10 @@ } | ||
/* Promise a -> (a -> b) -> Promise b */ | ||
/* Populate a promise with its final value. */ | ||
Promise.prototype.resolve = function (x) { | ||
this.value = x; | ||
this.resolved = true; | ||
this.emitter.emit('resolved', x); | ||
}; | ||
/* map :: Promise a -> (a -> b) -> Promise b */ | ||
Promise.prototype.map = function (f) { | ||
@@ -19,7 +26,6 @@ var promise = new Promise(); | ||
if (this.resolved) { | ||
promise.resolve(f.apply(null, this.value)); | ||
promise.resolve(f(this.value)); | ||
} else { | ||
this.emitter.once('resolved', function () { | ||
var x = f.apply(null, [].slice.call(arguments)); | ||
promise.resolve(x); | ||
this.emitter.once('resolved', function (x) { | ||
promise.resolve(f(x)); | ||
}); | ||
@@ -31,10 +37,15 @@ } | ||
/* Promise a -> (a -> Promise b) -> Promise b */ | ||
/* chain :: Promise a -> (a -> Promise b) -> Promise b */ | ||
Promise.prototype.chain = function (f) { | ||
if (this.resolved) { | ||
return f.apply(null, this.value); | ||
return f(this.value); | ||
} else { | ||
var promise = new Promise(); | ||
/* Promise a -> (a -> Promise b) -> (Promise (Promise b)) -> (Promise b) */ | ||
/* Map over the given Promise a with (a -> Promise b), returning | ||
* a new Promise (Promise b). Map over that, thereby gaining access to | ||
* the inner Promise b. Map over that in order to get to the inner value | ||
* of b and resolve another promise with it. Return that promise as | ||
* it is equivalent to Promise b. | ||
*/ | ||
this.map(f).map(function (x) { | ||
@@ -50,13 +61,9 @@ x.map(function (y) { | ||
/* Promise a -> Promise b -> Promise [a b] */ | ||
/* concat :: Promise a -> Promise a */ | ||
Promise.prototype.concat = function (other) { | ||
var promise = new Promise(); | ||
this.map(function () { | ||
var args = [].slice.call(arguments); | ||
other.map(function () { | ||
var otherArgs = [].slice.call(arguments); | ||
promise.resolve.apply(promise, args.concat(otherArgs)); | ||
this.map(function (x) { | ||
other.map(function (y) { | ||
promise.resolve(x.concat(y)); | ||
}); | ||
@@ -68,9 +75,3 @@ }); | ||
Promise.prototype.resolve = function () { | ||
var values = [].slice.call(arguments); | ||
this.value = values; | ||
this.resolved = true; | ||
this.emitter.emit.apply(this.emitter, ['resolved'].concat(values)); | ||
}; | ||
/* ap :: Promise (a -> b) -> Promise a -> Promise b */ | ||
Promise.prototype.ap = function (m) { | ||
@@ -82,2 +83,37 @@ return this.chain(function (f) { | ||
/* empty :: Promise a -> Promise a */ | ||
Promise.prototype.empty = function () { | ||
return Promise.of(this.value.empty ? this.value.empty() : this.value.constructor.empty()); | ||
}; | ||
/* conjoin :: Promise a -> Promise b -> Promise [a b] | ||
* conjoin :: Promise [a] -> Promise b -> Promise [a b] | ||
* conjoin :: Promise a -> Promise [b] -> Promise [a b] | ||
* conjoin :: Promise [a] -> Promise [b] -> Promise [a b] | ||
*/ | ||
Promise.prototype.conjoin = function (other) { | ||
var list = function (x) { return [].concat(x); }; | ||
return this.map(list).concat(other.map(list)); | ||
}; | ||
/* combine :: Promise [a] -> Promise [b] -> Promise [[a] [b]] | ||
* combine :: Promise [a] -> Promise b -> Promise [[a] b] | ||
* combine :: Promise a -> Promise [b] -> Promise [a [b]] | ||
* combine :: Promise a -> Promise b -> Promise [a b] | ||
*/ | ||
Promise.prototype.combine = function (other) { | ||
var wrap = function (x) { return [x]; }; | ||
return this.map(wrap).concat(other.map(wrap)); | ||
}; | ||
/* explode :: Promise a -> (a -> b) -> Promise b */ | ||
Promise.prototype.explode = function (f) { | ||
return this.map(function (x) { | ||
return f.apply(null, x); | ||
}); | ||
}; | ||
/* of :: a -> Promise a */ | ||
Promise.of = function (x) { | ||
@@ -87,9 +123,12 @@ return new Promise(x); | ||
Promise.empty = function () { | ||
var promise = new Promise(); | ||
promise.resolve(); | ||
return promise; | ||
/* A Monoid interface for Array. */ | ||
Array.empty = function () { | ||
return []; | ||
}; | ||
/* A Monoid interface for String. */ | ||
String.empty = function () { | ||
return ''; | ||
} | ||
exports.Promise = Promise; |
@@ -7,4 +7,5 @@ { | ||
"keywords": ["promises", "monad", "functor"], | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"main": "./lib/pacta.js", | ||
"dependencies": {}, | ||
"devDependencies": { "mocha": "1.10.0" }, | ||
@@ -11,0 +12,0 @@ "scripts": { "test": "mocha" }, |
@@ -10,13 +10,49 @@ # pacta [![Build Status](https://travis-ci.org/mudge/pacta.png?branch=master)](https://travis-ci.org/mudge/pacta) | ||
* [Semigroups](https://github.com/puffnfresh/fantasy-land#semigroup); | ||
* [Monoids](https://github.com/puffnfresh/fantasy-land#monoid); | ||
* [Functors](https://github.com/puffnfresh/fantasy-land#functor); | ||
* [Applicative](https://github.com/puffnfresh/fantasy-land#applicative); | ||
* [Chains](https://github.com/puffnfresh/fantasy-land#chain); | ||
* [Monads](https://github.com/puffnfresh/fantasy-land#monad). | ||
* [Semigroups](https://github.com/puffnfresh/fantasy-land#semigroup) (through | ||
`Promise#concat` which concatenates promises containing semigroups such as | ||
arrays and strings); | ||
* [Monoids](https://github.com/puffnfresh/fantasy-land#monoid) (through | ||
`Promise#empty` which returns an empty version of a promise that contains a | ||
monoid); | ||
* [Functors](https://github.com/puffnfresh/fantasy-land#functor) (through | ||
`Promise#map`); | ||
* [Applicative](https://github.com/puffnfresh/fantasy-land#applicative) | ||
(through `Promise#ap` and `Promise.of`); | ||
* [Chains](https://github.com/puffnfresh/fantasy-land#chain) (through `Promise#chain`); | ||
* [Monads](https://github.com/puffnfresh/fantasy-land#monad) (through all of | ||
the above). | ||
Above that, Pacta also provides: | ||
* `conjoin` to concatenate promises into a list of values regardless of their | ||
original type meaning that non-Monoid types can be combined with others | ||
(e.g. a promise of `'foo'` can be conjoined with `[1, 2]` to produce | ||
`['foo', 1, 2]`); | ||
* `combine` to conjoin promises without flattening lists (e.g. combining a | ||
promise of `[1, 2]` and `[3]` will give a promise of `[[1, 2], [3]]` instead | ||
of `[1, 2, 3]` as it would with `concat` and `conjoin`); | ||
* `explode` to map over a promise's value but, instead of receiving a single | ||
value, explode the promise's value into seperate arguments: | ||
```javascript | ||
Promise.of([1, 2]).explode(function (x, y) { | ||
console.log(x); //=> 1 | ||
console.log(y); //=> 2 | ||
}); | ||
``` | ||
It also defines a monoid interface for `Array` and `String`, implementing | ||
`empty` such that: | ||
```javascript | ||
Array.empty(); //=> [] | ||
String.empty(); //=> "" | ||
``` | ||
Note that Pacta does not handle errors or the concept of a failed promise as | ||
yet. | ||
See [the test | ||
See the [HTTP client | ||
example](https://github.com/mudge/pacta/blob/master/example/codenames.js) and | ||
[the test | ||
suite](https://github.com/mudge/pacta/blob/master/test/pacta_test.js) for more | ||
@@ -35,20 +71,31 @@ information. | ||
p.map(console.log); //=> "Foo" | ||
p.map(function (x) { | ||
return x + '!'; | ||
}).map(console.log); //=> "Foo!" | ||
var p2 = new Promise(); | ||
setTimeout(function () { | ||
p2.resolve('bar'); | ||
p2.resolve(['bar']); | ||
}, 500); | ||
var p3 = Promise.of('baz'); | ||
var p3 = Promise.of(['baz']); | ||
p.map(console.log); //=> "Foo" | ||
p2.concat(p3).map(function (x) { | ||
console.log(x); //=> [ 'bar', 'baz' ] | ||
}); | ||
p.map(function (x) { | ||
return x + '!'; | ||
}).map(console.log); //=> "Foo!" | ||
p.conjoin(p2).map(function (x) { | ||
console.log(x); //=> [ 'Foo', 'bar' ] | ||
}); | ||
p.concat(p2).concat(p3).map(function (x, y, z) { | ||
console.log('p says: ' + x); //=> "p says: Foo" | ||
console.log('p2 says: ' + y); //=> "p2 says: bar" | ||
console.log('p3 says: ' + z); //=> "p3 says: baz" | ||
p.combine(p2).map(function (x) { | ||
console.log(x); //=> [ 'Foo', [ 'bar' ] ] | ||
}); | ||
p.combine(p2).explode(function (x, y) { | ||
console.log(x); //=> Foo | ||
console.log(y); //=> [ 'bar' ] | ||
}); | ||
``` |
@@ -23,3 +23,3 @@ var assert = require('assert'), | ||
p4 = new Promise('quux'); | ||
p4 = Promise.of('quux'); | ||
}); | ||
@@ -107,6 +107,10 @@ | ||
it('fulfils the associativity property of semigroups #1', function (done) { | ||
p.concat(p2).concat(p3).map(function (x, y, z) { | ||
assert.equal('foo', x); | ||
assert.equal('bar', y); | ||
assert.equal('baz', z); | ||
var p = Promise.of([1]), | ||
p2 = Promise.of([2]), | ||
p3 = Promise.of([3]); | ||
p.concat(p2).concat(p3).map(function (x) { | ||
assert.equal(1, x[0]); | ||
assert.equal(2, x[1]); | ||
assert.equal(3, x[2]); | ||
done(); | ||
@@ -117,9 +121,33 @@ }); | ||
it('fulfils the associativity property of semigroups #2', function (done) { | ||
p.concat(p2.concat(p3)).map(function (x, y, z) { | ||
assert.equal('foo', x); | ||
assert.equal('bar', y); | ||
assert.equal('baz', z); | ||
var p = Promise.of([1]), | ||
p2 = Promise.of([2]), | ||
p3 = Promise.of([3]); | ||
p.concat(p2.concat(p3)).map(function (x) { | ||
assert.equal(1, x[0]); | ||
assert.equal(2, x[1]); | ||
assert.equal(3, x[2]); | ||
done(); | ||
}); | ||
}); | ||
it('fulfils the identity of a semigroup', function (done) { | ||
var p = Promise.of([1]), | ||
p2 = Promise.of([2]), | ||
p3 = Promise.of([3]); | ||
p.concat(p2).concat(p3).map(function (x) { | ||
return x; | ||
}).map(function (x) { | ||
assert.deepEqual([1, 2, 3], x); | ||
done(); | ||
}); | ||
}); | ||
it('concatenates any monoid including strings', function (done) { | ||
p.concat(p2).concat(p3).map(function (x) { | ||
assert.equal('foobarbaz', x); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -226,6 +254,8 @@ | ||
describe('.empty', function () { | ||
describe('#empty', function () { | ||
it('conforms to the right identity', function (done) { | ||
p.concat(Promise.empty()).map(function (x) { | ||
assert.equal('foo', x); | ||
var p = Promise.of([1]); | ||
p.concat(p.empty()).map(function (x) { | ||
assert.deepEqual([1], x); | ||
done(); | ||
@@ -236,4 +266,6 @@ }); | ||
it('conforms to the left identity', function (done) { | ||
Promise.empty().concat(p).map(function (x) { | ||
assert.equal('foo', x); | ||
var p = Promise.of([1]); | ||
p.empty().concat(p).map(function (x) { | ||
assert.deepEqual([1], x); | ||
done(); | ||
@@ -243,2 +275,95 @@ }); | ||
}); | ||
describe('#conjoin', function () { | ||
it('concatenates values into a list regardless of type', function (done) { | ||
p.conjoin(p2).conjoin(p3).map(function (x) { | ||
assert.deepEqual(['foo', 'bar', 'baz'], x); | ||
done(); | ||
}); | ||
}); | ||
it('concatenates values into a list even if already a list', function (done) { | ||
var p = Promise.of([1]), | ||
p2 = Promise.of([2, 3]), | ||
p3 = Promise.of([4]); | ||
p.conjoin(p2).conjoin(p3).map(function (x) { | ||
assert.deepEqual([1, 2, 3, 4], x); | ||
done(); | ||
}); | ||
}); | ||
it('concatenates values of mixed types', function (done) { | ||
var p2 = Promise.of([2, 3]); | ||
p.conjoin(p2).map(function (x) { | ||
assert.deepEqual(['foo', 2, 3], x); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('#combine', function () { | ||
it('conjoins promises without flattening lists', function (done) { | ||
var p = Promise.of([1]), | ||
p2 = Promise.of([2, 3]); | ||
p.combine(p2).map(function (x) { | ||
assert.deepEqual([[1], [2, 3]], x); | ||
done(); | ||
}); | ||
}); | ||
it('conjoins non-list promises', function (done) { | ||
p.combine(p2).map(function (x) { | ||
assert.deepEqual(['foo', 'bar'], x); | ||
done(); | ||
}); | ||
}); | ||
it('conjoins both list and non-list promises', function (done) { | ||
var p2 = Promise.of([2, 3]); | ||
p.combine(p2).map(function (x) { | ||
assert.deepEqual(['foo', [2, 3]], x); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe('#explode', function () { | ||
it('calls the given function with each value of the Promise', function (done) { | ||
var p = Promise.of([1, 2, 3]); | ||
p.explode(function (x, y, z) { | ||
assert.equal(1, x); | ||
assert.equal(2, y); | ||
assert.equal(3, z); | ||
done(); | ||
}); | ||
}); | ||
it('returns a promise with a single value', function (done) { | ||
var p = Promise.of([1, 2, 3]); | ||
p.explode(function (x, y, z) { | ||
return x + y + z; | ||
}).map(function (x) { | ||
assert.equal(6, x); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('Array', function () { | ||
describe('.empty', function () { | ||
assert.deepEqual([], Array.empty()); | ||
}); | ||
}); | ||
describe('String', function () { | ||
describe('.empty', function () { | ||
assert.equal('', String.empty()); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20990
8
446
100