Skip to content

Commit

Permalink
Update API to match eventual-cljs better.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gozala committed Apr 24, 2012
1 parent 07b276c commit f90e247
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 77 deletions.
165 changes: 111 additions & 54 deletions core.js
Expand Up @@ -9,7 +9,9 @@ var protocol = require('protocol/core').protocol
var reflection = require('reflection/core'),
string = reflection.string, array = reflection.array,
object = reflection.object, fn = reflection.fn
var nil

function identity(value) { return value }
function attempt(f) {
/**
Returns wrapper function that delegates to `f`. If `f` throws then captures
Expand All @@ -22,82 +24,137 @@ function attempt(f) {
}
}

var IEventual = protocol({
deliver: [ protocol, 'value' ]
var ISecure = protocol({
privates: [ protocol, 'accessor' ]
})
exports.IEventual = IEventual
exports.deliver = IEventual.deliver
exports.ISecure = ISecure
var privates = ISecure.privates

var IPending = protocol({
isRealized: [ protocol ]
})
var isRealized = IPending.isRealized

var IReactor = protocol({
wait: [ protocol, 'deliver:Function', 'reject:Function' ],
var IValue = protocol({
valueOf: [ protocol ]
})
exports.IReactor = IReactor
exports.IValue = IValue
var valueOf = IValue.valueOf

var wait = IReactor.wait
exports.wait = wait
var IDeferred = protocol({
realize: [ protocol, 'value' ]
})
exports.IDeferred = IDeferred

IReactor(Object, {
wait: function(reactor, deliver, reject) {
return attempt(deliver)(reactor)
}
var IEventual = protocol({
then: [ protocol, 'realized:Function', 'rejected:Function' ],
when: [ protocol, 'realized:Function', 'rejected:Function' ]
})
exports.IEventual = IEventual
var then = IEventual.then, when = IEventual.when

IReactor(Error, {
wait: function(reactor, deliver, reject) {
return attempt(reject)(reactor)
IValue(Object, {
value: function($) {
return $.valueOf()
}
})
IEventual(Object, {
then: function(object, realized, rejected) {
var deferred = defer()
realized = attempt(realized || identity)
rejected = attempt(rejected || identity)
when(object, function($) {
realize(deferred, realized($))
}, function($) {
realize(deferred, rejected($))
})
return valueOf(deferred)
},
when: function(object, realize, _) {
realize(object)
}
})

function valueOf(object) { return object.valueOf(valueOf) }
function identity(value) { return value }
IEventual(Error, {
when: function($, _, rejected) { attempt(rejected)($) }
})

function Reactor() {
var state = { pending: [] }
return Object.create(Reactor.prototype, {
valueOf: { value: function($) {
return $ === valueOf ? state : this
var DeferredKey = {}
function Deferred(state, observers) {
var privates = { state: state, observers: observers }
return Object.defineProperties(this, {
valueOf: { value: function(key) {
return key === DeferredKey ? privates : this
}}
})
}
exports.Reactor = Reactor
IEventual(Reactor, {
deliver: function deliver(reactor, value) {
var state = valueOf(reactor), pending = state.pending
if (pending) {
state.result = value
while (pending.length) wait.apply(null, [value].concat(pending.shift()))
state.pending = false
}
exports.Deferred = Deferred
ISecure(Deferred, {
privates: function($) { return $.valueOf(DeferredKey) }
})
IPending(Deferred, {
isRealized: function($) { return privates($).state.done }
})
IValue(Deferred, {
valueOf: function($) { return isRealized($) ? privates($).state.value : $ }
})
IEventual(Deferred, {
when: function($, realized, rejected) {
if (isRealized($))
when(valueOf($), realized, rejected)
else
privates($).observers.push({ realize: realized, reject: rejected })
}
})
IReactor(Reactor, {
wait: function(reactor, deliver, reject) {
var state = valueOf(reactor), pending = state.pending, result = state.result
if (!pending) return wait(result, deliver, reject)
result = Reactor()
deliver = deliver ? attempt(deliver) : identity
reject = reject ? attempt(reject) : identity
pending.push([
function delivered(value) { IEventual.deliver(result, deliver(value)) },
function rejected(error) { IEventual.deliver(result, reject(error)) }
])
return result
IDeferred(Deferred, {
realize: function($, value) {
if (!isRealized($)) {
var internals = privates($)
internals.state = { done: true , value: value }
internals.observers.forEach(function(observer) {
when(value, observer.realize, observer.reject)
})
internals.observers = null
}
}
})

function eventual(f) {
return function() {
var result = array.reduce(arguments, function(items, item) {
return wait(item, function(value) {
return wait(items, function(values) {
return array.concat([ value ], values)
})
var realize = IDeferred.realize
exports.realize = realize

function defer() {
return new Deferred({ done: false }, [])
}
exports.defer = defer

function group(promises) {
return array.reduce(promises, function(promises, promise) {
return then(promise, function(value) {
return then(promises, function(values) {
return array.concat(values, [ value ])
})
}, [])
return wait(result, function(args) {
return f.apply(f, args);
})
}, [])
}

function go(f/*, rest */) {
return then(group(arguments), function(params) {
var f = params.shift()
return f.apply(f, params)
})
}
exports.go = go

function recover(f, eventual) {
return then(eventual, identity, f)
}
exports.recover = recover

function eventual(f) {
return function eventually() {
var params = array.slice(arguments)
params.unshift(f)
return go.apply(go, params)
}
}
exports.eventual = eventual
Expand Down
46 changes: 23 additions & 23 deletions tests/test-all.js
Expand Up @@ -9,17 +9,17 @@

var core = require('../core'),
eventual = core.eventual,
deliver = core.deliver,
wait = core.wait,
Reactor = core.Reactor
realize = core.realize,
go = core.go,
defer = core.defer

var sum = eventual(function(a, b) { return a + b })

exports['test non-eventual values'] = function(assert) {
assert.equal(sum(2, 3), 5, 'call on non-eventuals returns value')
}

exports['test wait non-eventual'] = function(assert) {
exports['test go non-eventual'] = function(assert) {
;[
{ a: 1 },
[ 'b', 2 ],
Expand All @@ -31,20 +31,20 @@ exports['test wait non-eventual'] = function(assert) {
undefined,
null
].forEach(function(expected) {
wait(expected, function(actual) {
assert.equal(actual, expected, 'wait can be called on: ' + expected)
})
go(function(actual) {
assert.equal(actual, expected, 'go can be called on: ' + expected)
}, expected)
})
}

exports['test delivered eventuals'] = function(assert) {
var a = Reactor(), b = 3
deliver(a, 1)
var a = defer(), b = 3
realize(a, 1)

assert.equal(sum(a, b), 4, 'call on delivered eventual returns value')
}

exports['test wait on eventuals'] = function(assert) {
exports['test go on eventuals'] = function(assert) {
;[
{ a: 1 },
[ 'b', 2 ],
Expand All @@ -56,36 +56,36 @@ exports['test wait on eventuals'] = function(assert) {
undefined,
null
].forEach(function(expected) {
var value = Reactor()
deliver(value, expected)
wait(value, function(actual) {
assert.equal(actual, expected, 'wait works with eventual: ' + expected)
})
var value = defer()
realize(value, expected)
go(function(actual) {
assert.equal(actual, expected, 'go works with eventual: ' + expected)
}, value)
})
}

exports['test undelivered eventuals'] = function(assert) {
var expected = 7
var a = Reactor()
var a = defer()
var b = sum(a, 1)

assert.ok(typeof(b) === 'object', 'call on non-delivered returns eventual')

var c = sum(b, 3)

wait(a, function(value) {
go(function(value) {
assert.equal(value, expected, 'eventual resolved as expected')
})
}, a)

wait(b, function(value) {
go(function(value) {
assert.equal(value, expected + 1, 'eventual operation resolved as expected')
})
}, b)

wait(c, function(value) {
go(function(value) {
assert.equal(value, expected + 1 + 3, 'eventuals chain as expected')
})
}, c)

deliver(a, expected)
realize(a, expected)
}


Expand Down

0 comments on commit f90e247

Please sign in to comment.