Comparing version 0.7.1 to 0.8.0
@@ -14,7 +14,7 @@ /** | ||
reaction(r: Reaction<T>): Reaction<T>; | ||
reaction(f: (value: T) => void): Reaction<T>; | ||
reactor(r: Reactor<T>): Reactor<T>; | ||
reactor(f: (value: T) => void): Reactor<T>; | ||
react(r: Reaction<T>): Reaction<T>; | ||
react(f: (value: T) => void): Reaction<T>; | ||
react(r: Reactor<T>): Reactor<T>; | ||
react(f: (value: T) => void): Reactor<T>; | ||
@@ -54,12 +54,16 @@ get(): T; | ||
export class Reaction<T> { | ||
export class Reactor<T> { | ||
constructor (); | ||
start(): Reaction<T>; | ||
start(): Reactor<T>; | ||
stop(): Reaction<T>; | ||
stop(): Reactor<T>; | ||
force(): Reaction<T>; | ||
force(): Reactor<T>; | ||
isActive(): boolean; | ||
orphan(): Reactor<T>; | ||
react(value: T): void; | ||
@@ -120,3 +124,3 @@ | ||
function isReaction(obj: any): boolean; | ||
function isReactor(obj: any): boolean; | ||
@@ -123,0 +127,0 @@ function withEquality(equals: (a: any, b: any) => boolean): any; |
@@ -151,3 +151,3 @@ // UMD loader | ||
function gc_mark(node, reactions) { | ||
function gc_mark(node, reactors) { | ||
// make everything unstable | ||
@@ -158,3 +158,3 @@ if (node._type === types_REACTION) { | ||
} | ||
reactions.push(node); | ||
reactors.push(node); | ||
} else { | ||
@@ -165,3 +165,3 @@ for (var i = node._children.length; i--;) { | ||
child._state = gc_UNSTABLE; | ||
gc_mark(child, reactions); | ||
gc_mark(child, reactors); | ||
} | ||
@@ -190,24 +190,29 @@ } | ||
case gc_UNSTABLE: | ||
// unstable means the node was not visited during | ||
// the react phase, which means we kick it out of the | ||
// graph. | ||
if (node._type === types_REACTION) { | ||
// only happens when reaction created in transaction. see issue #14 | ||
node._state = gc_STABLE; | ||
} else { | ||
// unstable means the node was not visited during | ||
// the react phase, which means we kick it out of the | ||
// graph. | ||
// but first we check if all of its parents were unchanged | ||
// if so, we can avoid recalculating it in future by | ||
// caching its parents' current values. | ||
var stashedParentStates = []; | ||
for (i = node._parents.length; i--;) { | ||
var parent = node._parents[i]; | ||
if (parent._state !== gc_UNCHANGED) { | ||
// nope, its parents either have changed or weren't visited, | ||
// so we have to orphan this node | ||
node._state = gc_ORPHANED; | ||
break; | ||
// but first we check if all of its parents were unchanged | ||
// if so, we can avoid recalculating it in future by | ||
// caching its parents' current values. | ||
var stashedParentStates = []; | ||
for (i = node._parents.length; i--;) { | ||
var parent = node._parents[i]; | ||
if (parent._state !== gc_UNCHANGED) { | ||
// nope, its parents either have changed or weren't visited, | ||
// so we have to orphan this node | ||
node._state = gc_ORPHANED; | ||
break; | ||
} | ||
stashedParentStates.push([parent, parent._value]); | ||
} | ||
stashedParentStates.push([parent, parent._value]); | ||
if (node._state !== gc_ORPHANED) { | ||
node._state = gc_DISOWNED; | ||
node._parents = stashedParentStates; | ||
} | ||
} | ||
if (node._state !== gc_ORPHANED) { | ||
node._state = gc_DISOWNED; | ||
node._parents = stashedParentStates; | ||
} | ||
break; | ||
@@ -352,44 +357,98 @@ case gc_STABLE: | ||
function reactionBase (parent, control) { | ||
function reactorBase (parent, control) { | ||
return { | ||
control: control, | ||
parent: parent, | ||
control: control, // the actual object the user gets | ||
parent: parent, // the parent derivable | ||
parentReactor: null, | ||
dependentReactors: [], | ||
_state: gc_STABLE, | ||
active: false, | ||
active: false, // whether or not listening for changes in parent | ||
_type: types_REACTION, | ||
uid: util_nextId(), | ||
reacting: false | ||
reacting: false, // whether or not reaction function being invoked | ||
stopping: false, | ||
yielding: false, // whether or not letting parentReactor react first | ||
} | ||
} | ||
var cycleMsg = "Cyclical Reactor Dependency! Not allowed!"; | ||
function stop (base) { | ||
util_removeFromArray(base.parent._children, base); | ||
base.active = false; | ||
base.control.onStop && base.control.onStop(); | ||
if (base.active) { | ||
if (base.stopping) { | ||
throw Error(cycleMsg); | ||
} | ||
try { | ||
base.stopping = true; | ||
while (base.dependentReactors.length) { | ||
var dr = base.dependentReactors.pop(); | ||
stop(dr); | ||
} | ||
} finally { | ||
util_removeFromArray(base.parent._children, base); | ||
if (base.parentReactor) { | ||
orphan(base); | ||
} | ||
base.active = false; | ||
base.stopping = false; | ||
} | ||
base.control.onStop && base.control.onStop(); | ||
} | ||
} | ||
var parentReactorStack = []; | ||
function start (base) { | ||
util_addToArray(base.parent._children, base); | ||
base.active = true; | ||
base.control.onStart && base.control.onStart(); | ||
base.parent._get(); | ||
if (!base.active) { | ||
util_addToArray(base.parent._children, base); | ||
base.active = true; | ||
base.parent._get(); | ||
// capture reactor dependency relationships | ||
var len = parentReactorStack.length; | ||
if (len > 0) { | ||
base.parentReactor = parentReactorStack[len - 1]; | ||
util_addToArray(base.parentReactor.dependentReactors, base); | ||
} | ||
base.control.onStart && base.control.onStart(); | ||
} | ||
} | ||
function reactions_maybeReact (base) { | ||
if (base._state === gc_UNSTABLE) { | ||
var parent = base.parent, parentState = parent._state; | ||
if (parentState === gc_UNSTABLE || | ||
parentState === gc_ORPHANED || | ||
parentState === gc_DISOWNED || | ||
parentState === gc_NEW) { | ||
parent._get(); | ||
function orphan (base) { | ||
if (base.parentReactor) { | ||
util_removeFromArray(base.parentReactor.dependentReactors, base); | ||
base.parentReactor = null; | ||
} | ||
} | ||
function reactors_maybeReact (base) { | ||
if (base.yielding) { | ||
throw Error(cycleMsg); | ||
} | ||
if (base.active && base._state === gc_UNSTABLE) { | ||
if (base.parentReactor !== null) { | ||
try { | ||
base.yielding = true; | ||
reactors_maybeReact(base.parentReactor); | ||
} finally { | ||
base.yielding = false; | ||
} | ||
} | ||
parentState = parent._state; | ||
// parent might have deactivated this one | ||
if (base.active) { | ||
var parent = base.parent, parentState = parent._state; | ||
if (parentState === gc_UNSTABLE || | ||
parentState === gc_ORPHANED || | ||
parentState === gc_DISOWNED || | ||
parentState === gc_NEW) { | ||
parent._get(); | ||
} | ||
parentState = parent._state; | ||
if (parentState === gc_UNCHANGED) { | ||
base._state = gc_STABLE; | ||
} else if (parentState === gc_CHANGED) { | ||
force(base); | ||
} else { | ||
throw new Error("invalid parent state: " + parentState); | ||
if (parentState === gc_UNCHANGED) { | ||
base._state = gc_STABLE; | ||
} else if (parentState === gc_CHANGED) { | ||
force(base); | ||
} else { | ||
throw new Error("invalid parent state: " + parentState); | ||
} | ||
} | ||
@@ -405,12 +464,14 @@ } | ||
base.reacting = true; | ||
parentReactorStack.push(base); | ||
base.control.react(base.parent._get()); | ||
} finally { | ||
parentReactorStack.pop(); | ||
base.reacting = false; | ||
} | ||
} else { | ||
throw new Error("No reaction function available."); | ||
throw new Error("No reactor function available."); | ||
} | ||
} | ||
function reactions_Reaction () { | ||
function reactors_Reactor () { | ||
/*jshint validthis:true */ | ||
@@ -420,11 +481,11 @@ this._type = types_REACTION; | ||
function reactions_createBase (control, parent) { | ||
function reactors_createBase (control, parent) { | ||
if (control._base) { | ||
throw new Error("This reaction has already been initialized"); | ||
throw new Error("This reactor has already been initialized"); | ||
} | ||
control._base = reactionBase(parent, control); | ||
control._base = reactorBase(parent, control); | ||
return control; | ||
} | ||
util_extend(reactions_Reaction.prototype, { | ||
util_extend(reactors_Reactor.prototype, { | ||
start: function () { | ||
@@ -442,8 +503,12 @@ start(this._base); | ||
}, | ||
isRunning: function () { | ||
isActive: function () { | ||
return this._base.active; | ||
}, | ||
orphan: function () { | ||
orphan(this._base); | ||
return this; | ||
} | ||
}) | ||
}); | ||
function reactions_StandardReaction (f) { | ||
function reactors_StandardReactor (f) { | ||
/*jshint validthis:true */ | ||
@@ -454,6 +519,6 @@ this._type = types_REACTION; | ||
util_extend(reactions_StandardReaction.prototype, reactions_Reaction.prototype); | ||
util_extend(reactors_StandardReactor.prototype, reactors_Reactor.prototype); | ||
function reactions_anonymousReaction (descriptor) { | ||
return util_extend(new reactions_Reaction(), descriptor); | ||
function reactors_anonymousReactor (descriptor) { | ||
return util_extend(new reactors_Reactor(), descriptor); | ||
} | ||
@@ -507,11 +572,11 @@ | ||
reaction: function (f) { | ||
reactor: function (f) { | ||
if (typeof f === 'function') { | ||
return reactions_createBase(new reactions_StandardReaction(f), this); | ||
} else if (f instanceof reactions_Reaction) { | ||
return reactions_createBase(f, this); | ||
return reactors_createBase(new reactors_StandardReactor(f), this); | ||
} else if (f instanceof reactors_Reactor) { | ||
return reactors_createBase(f, this); | ||
} else if (f && f.react) { | ||
return reactions_createBase(reactions_anonymousReaction(f), this); | ||
return reactors_createBase(reactors_anonymousReactor(f), this); | ||
} else { | ||
throw new Error("Unrecognized type for reaction " + f); | ||
throw new Error("Unrecognized type for reactor " + f); | ||
} | ||
@@ -521,3 +586,3 @@ }, | ||
react: function (f) { | ||
return this.reaction(f).start().force(); | ||
return this.reactor(f).start().force(); | ||
}, | ||
@@ -710,5 +775,5 @@ | ||
function processReactionQueue (rq) { | ||
function processReactorQueue (rq) { | ||
for (var i = rq.length; i--;) { | ||
reactions_maybeReact(rq[i]); | ||
reactors_maybeReact(rq[i]); | ||
} | ||
@@ -723,3 +788,3 @@ } | ||
this.inTxnValues = {}; | ||
this.reactionQueue = []; | ||
this.reactorQueue = []; | ||
} | ||
@@ -738,3 +803,3 @@ | ||
txnState.inTxnValues[atom._uid] = [atom, state]; | ||
gc_mark(atom, txnState.reactionQueue); | ||
gc_mark(atom, txnState.reactorQueue); | ||
} | ||
@@ -753,3 +818,3 @@ | ||
} else { | ||
// change root state and run reactions. | ||
// change root state and run reactors. | ||
for (i = keys.length; i--;) { | ||
@@ -761,3 +826,3 @@ atomValueTuple = this.inTxnValues[keys[i]]; | ||
processReactionQueue(this.reactionQueue); | ||
processReactorQueue(this.reactorQueue); | ||
@@ -828,5 +893,5 @@ // then sweep for a clean finish | ||
var reactionQueue = []; | ||
gc_mark(this, reactionQueue); | ||
processReactionQueue(reactionQueue); | ||
var reactorQueue = []; | ||
gc_mark(this, reactorQueue); | ||
processReactorQueue(reactorQueue); | ||
gc_sweep(this); | ||
@@ -910,3 +975,3 @@ } | ||
ticker: atom_ticker, | ||
Reaction: reactions_Reaction, | ||
Reactor: reactors_Reactor, | ||
isAtom: function (x) { | ||
@@ -926,3 +991,3 @@ return x && (x._type === types_ATOM || x._type === types_LENS); | ||
}, | ||
isReaction: function (x) { | ||
isReactor: function (x) { | ||
return x && x._type === types_REACTION; | ||
@@ -929,0 +994,0 @@ }, |
@@ -1,7 +0,8 @@ | ||
!function(t,n){"use strict";t&&"function"==typeof t.define&&t.define.amd?t.define(["exports"],n):n("undefined"!=typeof exports?exports:t.Derivable={})}(this,function(t){"use strict";function n(t){for(var n=1;n<arguments.length;n++)for(var e=arguments[n],r=Z(e),i=r.length;i--;){var u=r[i];t[u]=e[u]}return t}function e(t){return Object.prototype.toString.call(t).slice(8,-1)}function r(t,n){return t===n?0!==t||1/t===1/n:t!==t&&n!==n}function i(t,n){return Object.hasOwnProperty.call(n,t)}function u(t,n,a,o){var c=e(t);if(c!==e(n))return!1;if("Boolean"===c||"Number"===c||"String"===c)return"object"==typeof t?"object"==typeof n&&r(t.valueOf(),n.valueOf()):r(t,n);if(r(t,n))return!0;if("RegExp"===c)return t.source===n.source&&t.global===n.global&&t.ignoreCase===n.ignoreCase&&t.multiline===n.multiline&&t.sticky===n.sticky&&t.unicode===n.unicode;if(Object(t)===t){if("Date"===c&&t.getTime()!==n.getTime())return!1;var s=Z(t);if(s.length!==Z(n).length)return!1;for(var f=a.length-1;f>=0;){if(a[f]===t)return o[f]===n;f-=1}for(a[a.length]=t,o[o.length]=n,f=s.length-1;f>=0;){var l=s[f];if(!i(l,n)||!u(n[l],t[l],a,o))return!1;f-=1}return a.pop(),o.pop(),!0}return!1}function a(t,n){return t&&"function"==typeof t.equals?t.equals(n):u(t,n,[],[])}function o(t,n){var e=t.indexOf(n);0>e&&t.push(n)}function c(t,n){var e=t.indexOf(n);e>=0&&t.splice(e,1)}function s(t,n){return t.indexOf(n)>=0}function f(){return $++}function l(t,n){return Array.prototype.slice.call(t,n)}function h(t,n){if(t._type===ht){if(t.reacting)throw new Error("Cycle detected! Don't do this!");n.push(t)}else for(var e=t._children.length;e--;){var r=t._children[e];r._state!==ut&&(r._state=ut,h(r,n))}}function p(t){var n;switch(t._state){case et:case rt:for(n=t._children.length;n--;){var e=t._children[n];p(e),e._state!==at&&t._children.splice(n,1)}t._state=at;break;case ut:var r=[];for(n=t._parents.length;n--;){var i=t._parents[n];if(i._state!==rt){t._state=it;break}r.push([i,i._value])}t._state!==it&&(t._state=ot,t._parents=r);break;case at:case it:case ot:break;default: | ||
throw new Error("can't sweep state "+t._state)}}function _(t){var n=!1;switch(t._type){case st:t._state=at,n=!0;break;case ft:case lt:t._state=nt,t._value=tt,n=!0;break;case ht:t._state=at,n=!1}if(n){for(var e=t._children.length;e--;)_(t._children[e]);t._children=[]}}function v(t){return ct.push([]),t(),ct.pop()}function d(t){ct.length>0&&o(ct[ct.length-1],t)}function g(){throw dt}function y(){return{currentTxn:null}}function w(t){return null!==t.currentTxn}function k(t){return t.currentTxn}function b(t,n){n._parent=t.currentTxn,n._state=pt,t.currentTxn=n}function m(t,n){var e=t.currentTxn;if(t.currentTxn=e._parent,e._state!==pt)throw new Error("unexpected state: "+e._state);n(e)}function x(t){m(t,function(t){t._state=_t,t.onCommit&&t.onCommit()})}function E(t){m(t,function(t){t._state=vt,t.onAbort&&t.onAbort()})}function T(t,n,e){b(t,n);try{e(g)}catch(r){if(E(t),r!==dt)throw r;return}x(t)}function O(t,n){b(t,n());var e=!1;return{tick:function(){if(e)throw new Error("can't tick disposed ticker");x(t),b(t,n())},stop:function(){if(e)throw new Error("ticker already disposed");x(t)}}}function j(t,n){return{control:n,parent:t,_state:at,active:!1,_type:ht,uid:f(),reacting:!1}}function A(t){c(t.parent._children,t),t.active=!1,t.control.onStop&&t.control.onStop()}function V(t){o(t.parent._children,t),t.active=!0,t.control.onStart&&t.control.onStart(),t.parent._get()}function q(t){if(t._state===ut){var n=t.parent,e=n._state;if((e===ut||e===it||e===ot||e===nt)&&n._get(),e=n._state,e===rt)t._state=at;else{if(e!==et)throw new Error("invalid parent state: "+e);C(t)}}}function C(t){if(!t.control.react)throw new Error("No reaction function available.");t._state=at;try{t.reacting=!0,t.control.react(t.parent._get())}finally{t.reacting=!1}}function D(){this._type=ht}function S(t,n){if(t._base)throw new Error("This reaction has already been initialized");return t._base=j(n,t),t}function R(t){this._type=ht,this.react=t}function N(t){return n(new D,t)}function G(t,n){var e={derive:function(n,e,r,i,u){var a=this;switch(arguments.length){ | ||
case 0:return a;case 1:return t.derivation(function(){return n(a.get())});case 2:return t.derivation(function(){return n(a.get(),t.unpack(e))});case 3:return t.derivation(function(){return n(a.get(),t.unpack(e),t.unpack(r))});case 4:return t.derivation(function(){return n(a.get(),t.unpack(e),t.unpack(r),t.unpack(i))});case 5:return t.derivation(function(){return n(a.get(),t.unpack(e),t.unpack(r),t.unpack(i),t.unpack(u))});default:var o=[a].concat(l(arguments,1));return t.derivation(function(){return n.apply(null,o.map(t.unpack))})}},reaction:function(t){if("function"==typeof t)return S(new R(t),this);if(t instanceof D)return S(t,this);if(t&&t.react)return S(N(t),this);throw new Error("Unrecognized type for reaction "+t)},react:function(t){return this.reaction(t).start().force()},get:function(){return d(this),this._get()},is:function(e){return t.lift(n.equals)(this,e)},and:function(n){return this.derive(function(e){return e&&t.unpack(n)})},or:function(n){return this.derive(function(e){return e||t.unpack(n)})},then:function(n,e){return this.derive(function(r){return t.unpack(r?n:e)})},some:function(n,e){return this.derive(function(r){return t.unpack(null===r||void 0===r?e:n)})},not:function(){return this.derive(function(t){return!t})}};return e["switch"]=function(){var e=arguments;return this.derive(function(r){var i;for(i=0;e.length-1>i;i+=2)if(n.equals(r,t.unpack(e[i])))return t.unpack(e[i+1]);return i===e.length-1?t.unpack(e[i]):void 0})},e}function z(t,n){return{_clone:function(){return t.derivation(this._deriver)},_forceGet:function(){var t,e=this,r=v(function(){var t=e._deriver();e._state=n.equals(t,e._value)?rt:et,e._value=t});for(t=this._parents.length;t--;){var i=this._parents[t];s(r,i)||c(i._children,this)}for(this._parents=r,t=r.length;t--;)o(r[t]._children,this)},_get:function(){var t,e;t:switch(this._state){case nt:case it:this._forceGet();break;case ut:for(t=0;this._parents.length>t;t++){e=this._parents[t];var r=e._state;if((r===ut||r===it||r===ot)&&e._get(),r=e._state,r===et){this._forceGet();break t} | ||
if(r!==at&&r!==rt)throw new Error("invalid parent mode: "+r)}this._state=rt;break;case ot:var i=[];for(t=0;this._parents.length>t;t++){var u=this._parents[t],a=u[1];if(e=u[0],!n.equals(e._get(),a)){this._parents=[],this._forceGet();break t}i.push(e)}for(t=i.length;t--;)o(i[t]._children,this);this._parents=i,this._state=rt}return this._value}}}function I(t,n){return t._children=[],t._parents=[],t._deriver=n,t._state=nt,t._type=ft,t._value=tt,t}function Q(t,n){return{swap:function(t){var n=l(arguments,0);return n[0]=this.get(),this.set(t.apply(null,n))},lens:function(n){return t.lens(this,n)}}}function L(t,n){return{_clone:function(){return t.lens(this._parent,{get:this._getter,set:this._setter})},set:function(t){return this._parent.set(this._setter(this._parent._get(),t)),this}}}function B(t,n,e){return t._getter=e.get,t._setter=e.set,t._parent=n,t._type=lt,t}function F(t){for(var n=t.length;n--;)q(t[n])}function M(){this.inTxnValues={},this.reactionQueue=[]}function P(t,n){var e=t.inTxnValues[n._uid];return e?e[1]:n._value}function U(t,n,e){t.inTxnValues[n._uid]=[n,e],h(n,t.reactionQueue)}function W(t,n){return{_clone:function(){return t.atom(this._value)},withValidator:function(t){if(null===t)return this._clone();if("function"==typeof t){var n=this._clone(),e=this._validator;return n._validator=e?function(n){return t(n)&&e(n)}:t,n}throw new Error(".withValidator expects function or null")},validate:function(){this._validate(this.get())},_validate:function(t){var n=this._validator&&this._validator(t);if(this._validator&&n!==!0)throw new Error("Failed validation with value: '"+t+"'. Validator returned '"+n+"' ")},set:function(t){if(this._validate(t),!n.equals(t,this._value))if(this._state=et,w(gt))U(k(gt),this,t);else{this._value=t;var e=[];h(this,e),F(e),p(this)}return this},_get:function(){return w(gt)?P(k(gt),this):this._value}}}function H(t,n){return t._uid=f(),t._children=[],t._state=at,t._value=n,t._type=st,t}function J(t){T(gt,new M,t)}function K(t){return function(){var n,e=l(arguments,0),r=this;return J(function(){ | ||
n=t.apply(r,e)}),n}}function X(){wt?wt.refCount++:(wt=O(gt,function(){return new M}),wt.refCount=1);var t=!1;return{tick:function(){if(t)throw new Error("tyring to use ticker after release");wt.tick()},release:function(){if(t)throw new Error("ticker already released");0===--wt.refCount&&(wt.stop(),wt=null),t=!0}}}function Y(t){function e(t){var n=l(arguments,1);return i.derivation(function(){for(var e="",r=0;t.length>r;r++)e+=t[r],n.length>r&&(e+=i.unpack(n[r]));return e})}function r(t){if(i.isDerivable(t))return t.get();if(t instanceof Array)return t.map(r);if(t.constructor===Object){for(var n={},e=Z(t),u=e.length;u--;){var a=e[u];n[a]=r(t[a])}return n}return t}t=n({},kt,t||{});var i={transact:J,defaultEquals:a,transaction:K,ticker:X,Reaction:D,isAtom:function(t){return t&&(t._type===st||t._type===lt)},isDerivable:function(t){return t&&(t._type===st||t._type===lt||t._type===ft)},isDerivation:function(t){return t&&(t._type===ft||t._type===lt)},isLensed:function(t){return t&&t._type===lt},isReaction:function(t){return t&&t._type===ht}},u=G(i,t),o=Q(i,t),c=n({},o,u,W(i,t)),s=n({},u,z(i,t)),f=n({},o,s,L(i,t));return i.atom=function(t){return H(Object.create(c),t)},i.swap=function(t,n){var e=l(arguments,1);return e[0]=t.get(),t.set(n.apply(null,e))},i.derivation=function(t){return I(Object.create(s),t)},i.derive=function(t){if(t instanceof Array)return e.apply(null,arguments);if(arguments.length>0)return u.derive.apply(t,l(arguments,1));throw new Error("Wrong arity for derive. Expecting 1+ args")},i.lens=function(t,n){var e=Object.create(f);return B(I(e,function(){return n.get(t.get())}),t,n)},i.unpack=function(t){return i.isDerivable(t)?t.get():t},i.lift=function(t){return function(){var n=arguments,e=this;return i.derivation(function(){return t.apply(e,Array.prototype.map.call(n,i.unpack))})}},i.set=function(t,n){return t.set(n)},i.get=function(t){return t.get()},i.struct=function(t){if(t.constructor===Object||t instanceof Array)return i.derivation(function(){return r(t)});throw new Error("`struct` expects plain Object or Array"); | ||
},i.ifThenElse=function(t,n,e){return t.then(n,e)},i.ifThenElse=function(t,n,e){return i.derivation(function(){return i.unpack(i.unpack(t)?n:e)})},i.some=function(t,n,e){return i.derivation(function(){var r=i.unpack(t);return i.unpack(null===r||void 0===r?e:n)})},i.or=function(){var t=arguments;return i.derivation(function(){for(var n,e=0;t.length>e&&!(n=i.unpack(t[e]));e++);return n})},i.and=function(){var t=arguments;return i.derivation(function(){for(var n,e=0;t.length>e&&(n=i.unpack(t[e]),n);e++);return n})},i.not=function(t){return t.derive(function(t){return!t})},i.switchCase=function(t){return u["switch"].apply(t,l(arguments,1))},i}var Z=Object.keys,$=0,tt=Object.freeze({equals:function(){return!1}}),nt=0,et=1,rt=2,it=3,ut=4,at=5,ot=6,ct=[],st="ATOM",ft="DERIVATION",lt="LENS",ht="REACTION",pt=0,_t=1,vt=3,dt={};n(D.prototype,{start:function(){return V(this._base),this},stop:function(){return A(this._base),this},force:function(){return C(this._base),this},isRunning:function(){return this._base.active}}),n(R.prototype,D.prototype);var gt=y(),yt={push:function(){}};n(M.prototype,{onCommit:function(){var t,n,e=Z(this.inTxnValues);if(w(gt))for(t=e.length;t--;)n=this.inTxnValues[e[t]],n[0].set(n[1]);else{for(t=e.length;t--;)n=this.inTxnValues[e[t]],n[0]._value=n[1],h(n[0],yt);for(F(this.reactionQueue),t=e.length;t--;)p(this.inTxnValues[e[t]][0])}},onAbort:function(){if(!w(gt))for(var t=Z(this.inTxnValues),n=t.length;n--;)_(this.inTxnValues[t[n]][0])}});var wt=null,kt={equals:a};n(t,Y()),t.withEquality=function(t){return Y({equals:t})},t["default"]=t}); | ||
!function(t,n){"use strict";t&&"function"==typeof t.define&&t.define.amd?t.define(["exports"],n):n("undefined"!=typeof exports?exports:t.Derivable={})}(this,function(t){"use strict";function n(t){for(var n=1;n<arguments.length;n++)for(var e=arguments[n],r=$(e),i=r.length;i--;){var a=r[i];t[a]=e[a]}return t}function e(t){return Object.prototype.toString.call(t).slice(8,-1)}function r(t,n){return t===n?0!==t||1/t===1/n:t!==t&&n!==n}function i(t,n){return Object.hasOwnProperty.call(n,t)}function a(t,n,u,o){var c=e(t);if(c!==e(n))return!1;if("Boolean"===c||"Number"===c||"String"===c)return"object"==typeof t?"object"==typeof n&&r(t.valueOf(),n.valueOf()):r(t,n);if(r(t,n))return!0;if("RegExp"===c)return t.source===n.source&&t.global===n.global&&t.ignoreCase===n.ignoreCase&&t.multiline===n.multiline&&t.sticky===n.sticky&&t.unicode===n.unicode;if(Object(t)===t){if("Date"===c&&t.getTime()!==n.getTime())return!1;var s=$(t);if(s.length!==$(n).length)return!1;for(var f=u.length-1;f>=0;){if(u[f]===t)return o[f]===n;f-=1}for(u[u.length]=t,o[o.length]=n,f=s.length-1;f>=0;){var l=s[f];if(!i(l,n)||!a(n[l],t[l],u,o))return!1;f-=1}return u.pop(),o.pop(),!0}return!1}function u(t,n){return t&&"function"==typeof t.equals?t.equals(n):a(t,n,[],[])}function o(t,n){var e=t.indexOf(n);0>e&&t.push(n)}function c(t,n){var e=t.indexOf(n);e>=0&&t.splice(e,1)}function s(t,n){return t.indexOf(n)>=0}function f(){return tt++}function l(t,n){return Array.prototype.slice.call(t,n)}function h(t,n){if(t._type===pt){if(t.reacting)throw new Error("Cycle detected! Don't do this!");n.push(t)}else for(var e=t._children.length;e--;){var r=t._children[e];r._state!==ut&&(r._state=ut,h(r,n))}}function p(t){var n;switch(t._state){case rt:case it:for(n=t._children.length;n--;){var e=t._children[n];p(e),e._state!==ot&&t._children.splice(n,1)}t._state=ot;break;case ut:if(t._type===pt)t._state=ot;else{var r=[];for(n=t._parents.length;n--;){var i=t._parents[n];if(i._state!==it){t._state=at;break}r.push([i,i._value])}t._state!==at&&(t._state=ct,t._parents=r)}break;case ot: | ||
case at:case ct:break;default:throw new Error("can't sweep state "+t._state)}}function _(t){var n=!1;switch(t._type){case ft:t._state=ot,n=!0;break;case lt:case ht:t._state=et,t._value=nt,n=!0;break;case pt:t._state=ot,n=!1}if(n){for(var e=t._children.length;e--;)_(t._children[e]);t._children=[]}}function v(t){return st.push([]),t(),st.pop()}function d(t){st.length>0&&o(st[st.length-1],t)}function g(){throw gt}function y(){return{currentTxn:null}}function w(t){return null!==t.currentTxn}function k(t){return t.currentTxn}function b(t,n){n._parent=t.currentTxn,n._state=_t,t.currentTxn=n}function m(t,n){var e=t.currentTxn;if(t.currentTxn=e._parent,e._state!==_t)throw new Error("unexpected state: "+e._state);n(e)}function x(t){m(t,function(t){t._state=vt,t.onCommit&&t.onCommit()})}function E(t){m(t,function(t){t._state=dt,t.onAbort&&t.onAbort()})}function T(t,n,e){b(t,n);try{e(g)}catch(r){if(E(t),r!==gt)throw r;return}x(t)}function O(t,n){b(t,n());var e=!1;return{tick:function(){if(e)throw new Error("can't tick disposed ticker");x(t),b(t,n())},stop:function(){if(e)throw new Error("ticker already disposed");x(t)}}}function R(t,n){return{control:n,parent:t,parentReactor:null,dependentReactors:[],_state:ot,active:!1,_type:pt,uid:f(),reacting:!1,stopping:!1,yielding:!1}}function A(t){if(t.active){if(t.stopping)throw Error(yt);try{for(t.stopping=!0;t.dependentReactors.length;){var n=t.dependentReactors.pop();A(n)}}finally{c(t.parent._children,t),t.parentReactor&&V(t),t.active=!1,t.stopping=!1}t.control.onStop&&t.control.onStop()}}function j(t){if(!t.active){o(t.parent._children,t),t.active=!0,t.parent._get();var n=wt.length;n>0&&(t.parentReactor=wt[n-1],o(t.parentReactor.dependentReactors,t)),t.control.onStart&&t.control.onStart()}}function V(t){t.parentReactor&&(c(t.parentReactor.dependentReactors,t),t.parentReactor=null)}function q(t){if(t.yielding)throw Error(yt);if(t.active&&t._state===ut){if(null!==t.parentReactor)try{t.yielding=!0,q(t.parentReactor)}finally{t.yielding=!1}if(t.active){var n=t.parent,e=n._state;if((e===ut||e===at||e===ct||e===et)&&n._get(), | ||
e=n._state,e===it)t._state=ot;else{if(e!==rt)throw new Error("invalid parent state: "+e);C(t)}}}}function C(t){if(!t.control.react)throw new Error("No reactor function available.");t._state=ot;try{t.reacting=!0,wt.push(t),t.control.react(t.parent._get())}finally{wt.pop(),t.reacting=!1}}function D(){this._type=pt}function S(t,n){if(t._base)throw new Error("This reactor has already been initialized");return t._base=R(n,t),t}function N(t){this._type=pt,this.react=t}function G(t){return n(new D,t)}function z(t,n){var e={derive:function(n,e,r,i,a){var u=this;switch(arguments.length){case 0:return u;case 1:return t.derivation(function(){return n(u.get())});case 2:return t.derivation(function(){return n(u.get(),t.unpack(e))});case 3:return t.derivation(function(){return n(u.get(),t.unpack(e),t.unpack(r))});case 4:return t.derivation(function(){return n(u.get(),t.unpack(e),t.unpack(r),t.unpack(i))});case 5:return t.derivation(function(){return n(u.get(),t.unpack(e),t.unpack(r),t.unpack(i),t.unpack(a))});default:var o=[u].concat(l(arguments,1));return t.derivation(function(){return n.apply(null,o.map(t.unpack))})}},reactor:function(t){if("function"==typeof t)return S(new N(t),this);if(t instanceof D)return S(t,this);if(t&&t.react)return S(G(t),this);throw new Error("Unrecognized type for reactor "+t)},react:function(t){return this.reactor(t).start().force()},get:function(){return d(this),this._get()},is:function(e){return t.lift(n.equals)(this,e)},and:function(n){return this.derive(function(e){return e&&t.unpack(n)})},or:function(n){return this.derive(function(e){return e||t.unpack(n)})},then:function(n,e){return this.derive(function(r){return t.unpack(r?n:e)})},some:function(n,e){return this.derive(function(r){return t.unpack(null===r||void 0===r?e:n)})},not:function(){return this.derive(function(t){return!t})}};return e["switch"]=function(){var e=arguments;return this.derive(function(r){var i;for(i=0;e.length-1>i;i+=2)if(n.equals(r,t.unpack(e[i])))return t.unpack(e[i+1]);return i===e.length-1?t.unpack(e[i]):void 0})},e}function I(t,n){ | ||
return{_clone:function(){return t.derivation(this._deriver)},_forceGet:function(){var t,e=this,r=v(function(){var t=e._deriver();e._state=n.equals(t,e._value)?it:rt,e._value=t});for(t=this._parents.length;t--;){var i=this._parents[t];s(r,i)||c(i._children,this)}for(this._parents=r,t=r.length;t--;)o(r[t]._children,this)},_get:function(){var t,e;t:switch(this._state){case et:case at:this._forceGet();break;case ut:for(t=0;this._parents.length>t;t++){e=this._parents[t];var r=e._state;if((r===ut||r===at||r===ct)&&e._get(),r=e._state,r===rt){this._forceGet();break t}if(r!==ot&&r!==it)throw new Error("invalid parent mode: "+r)}this._state=it;break;case ct:var i=[];for(t=0;this._parents.length>t;t++){var a=this._parents[t],u=a[1];if(e=a[0],!n.equals(e._get(),u)){this._parents=[],this._forceGet();break t}i.push(e)}for(t=i.length;t--;)o(i[t]._children,this);this._parents=i,this._state=it}return this._value}}}function Q(t,n){return t._children=[],t._parents=[],t._deriver=n,t._state=et,t._type=lt,t._value=nt,t}function L(t,n){return{swap:function(t){var n=l(arguments,0);return n[0]=this.get(),this.set(t.apply(null,n))},lens:function(n){return t.lens(this,n)}}}function B(t,n){return{_clone:function(){return t.lens(this._parent,{get:this._getter,set:this._setter})},set:function(t){return this._parent.set(this._setter(this._parent._get(),t)),this}}}function F(t,n,e){return t._getter=e.get,t._setter=e.set,t._parent=n,t._type=ht,t}function M(t){for(var n=t.length;n--;)q(t[n])}function P(){this.inTxnValues={},this.reactorQueue=[]}function U(t,n){var e=t.inTxnValues[n._uid];return e?e[1]:n._value}function W(t,n,e){t.inTxnValues[n._uid]=[n,e],h(n,t.reactorQueue)}function H(t,n){return{_clone:function(){return t.atom(this._value)},withValidator:function(t){if(null===t)return this._clone();if("function"==typeof t){var n=this._clone(),e=this._validator;return n._validator=e?function(n){return t(n)&&e(n)}:t,n}throw new Error(".withValidator expects function or null")},validate:function(){this._validate(this.get())},_validate:function(t){ | ||
var n=this._validator&&this._validator(t);if(this._validator&&n!==!0)throw new Error("Failed validation with value: '"+t+"'. Validator returned '"+n+"' ")},set:function(t){if(this._validate(t),!n.equals(t,this._value))if(this._state=rt,w(kt))W(k(kt),this,t);else{this._value=t;var e=[];h(this,e),M(e),p(this)}return this},_get:function(){return w(kt)?U(k(kt),this):this._value}}}function J(t,n){return t._uid=f(),t._children=[],t._state=ot,t._value=n,t._type=ft,t}function K(t){T(kt,new P,t)}function X(t){return function(){var n,e=l(arguments,0),r=this;return K(function(){n=t.apply(r,e)}),n}}function Y(){mt?mt.refCount++:(mt=O(kt,function(){return new P}),mt.refCount=1);var t=!1;return{tick:function(){if(t)throw new Error("tyring to use ticker after release");mt.tick()},release:function(){if(t)throw new Error("ticker already released");0===--mt.refCount&&(mt.stop(),mt=null),t=!0}}}function Z(t){function e(t){var n=l(arguments,1);return i.derivation(function(){for(var e="",r=0;t.length>r;r++)e+=t[r],n.length>r&&(e+=i.unpack(n[r]));return e})}function r(t){if(i.isDerivable(t))return t.get();if(t instanceof Array)return t.map(r);if(t.constructor===Object){for(var n={},e=$(t),a=e.length;a--;){var u=e[a];n[u]=r(t[u])}return n}return t}t=n({},xt,t||{});var i={transact:K,defaultEquals:u,transaction:X,ticker:Y,Reactor:D,isAtom:function(t){return t&&(t._type===ft||t._type===ht)},isDerivable:function(t){return t&&(t._type===ft||t._type===ht||t._type===lt)},isDerivation:function(t){return t&&(t._type===lt||t._type===ht)},isLensed:function(t){return t&&t._type===ht},isReactor:function(t){return t&&t._type===pt}},a=z(i,t),o=L(i,t),c=n({},o,a,H(i,t)),s=n({},a,I(i,t)),f=n({},o,s,B(i,t));return i.atom=function(t){return J(Object.create(c),t)},i.swap=function(t,n){var e=l(arguments,1);return e[0]=t.get(),t.set(n.apply(null,e))},i.derivation=function(t){return Q(Object.create(s),t)},i.derive=function(t){if(t instanceof Array)return e.apply(null,arguments);if(arguments.length>0)return a.derive.apply(t,l(arguments,1));throw new Error("Wrong arity for derive. Expecting 1+ args"); | ||
},i.lens=function(t,n){var e=Object.create(f);return F(Q(e,function(){return n.get(t.get())}),t,n)},i.unpack=function(t){return i.isDerivable(t)?t.get():t},i.lift=function(t){return function(){var n=arguments,e=this;return i.derivation(function(){return t.apply(e,Array.prototype.map.call(n,i.unpack))})}},i.set=function(t,n){return t.set(n)},i.get=function(t){return t.get()},i.struct=function(t){if(t.constructor===Object||t instanceof Array)return i.derivation(function(){return r(t)});throw new Error("`struct` expects plain Object or Array")},i.ifThenElse=function(t,n,e){return t.then(n,e)},i.ifThenElse=function(t,n,e){return i.derivation(function(){return i.unpack(i.unpack(t)?n:e)})},i.some=function(t,n,e){return i.derivation(function(){var r=i.unpack(t);return i.unpack(null===r||void 0===r?e:n)})},i.or=function(){var t=arguments;return i.derivation(function(){for(var n,e=0;t.length>e&&!(n=i.unpack(t[e]));e++);return n})},i.and=function(){var t=arguments;return i.derivation(function(){for(var n,e=0;t.length>e&&(n=i.unpack(t[e]),n);e++);return n})},i.not=function(t){return t.derive(function(t){return!t})},i.switchCase=function(t){return a["switch"].apply(t,l(arguments,1))},i}var $=Object.keys,tt=0,nt=Object.freeze({equals:function(){return!1}}),et=0,rt=1,it=2,at=3,ut=4,ot=5,ct=6,st=[],ft="ATOM",lt="DERIVATION",ht="LENS",pt="REACTION",_t=0,vt=1,dt=3,gt={},yt="Cyclical Reactor Dependency! Not allowed!",wt=[];n(D.prototype,{start:function(){return j(this._base),this},stop:function(){return A(this._base),this},force:function(){return C(this._base),this},isActive:function(){return this._base.active},orphan:function(){return V(this._base),this}}),n(N.prototype,D.prototype);var kt=y(),bt={push:function(){}};n(P.prototype,{onCommit:function(){var t,n,e=$(this.inTxnValues);if(w(kt))for(t=e.length;t--;)n=this.inTxnValues[e[t]],n[0].set(n[1]);else{for(t=e.length;t--;)n=this.inTxnValues[e[t]],n[0]._value=n[1],h(n[0],bt);for(M(this.reactorQueue),t=e.length;t--;)p(this.inTxnValues[e[t]][0])}},onAbort:function(){if(!w(kt))for(var t=$(this.inTxnValues),n=t.length;n--;)_(this.inTxnValues[t[n]][0]); | ||
}});var mt=null,xt={equals:u};n(t,Z()),t.withEquality=function(t){return Z({equals:t})},t["default"]=t}); | ||
//# sourceMappingURL=derivable.min.js.map |
{ | ||
"name": "derivable", | ||
"version": "0.7.1", | ||
"version": "0.8.0", | ||
"description": "Functional Reactive State for JavaScript & TypeScript", | ||
@@ -5,0 +5,0 @@ "author": "David Sheldrick", |
@@ -33,13 +33,14 @@ <h1 align="center">DerivableJS</h1> | ||
Derivables are a radically simplifying paradigm shift in our approach to managing application state. They are liberating like switching from manual memory management to garbage collection, and they are profound like switching from OO to FP. | ||
Derivables make it trivial to maintain consistent (i.e. sense-making) state at all times without requiring that it be kept all in one place. This is a huge win for those of us who develop complex systems with lots of moving parts because it eradicates an entire class of subtle-but-devastating bugs along with all the incidental complexity they fed upon, allowing us to spend more quality time getting intimate with our problem domain. | ||
Sickening hyperbole aside, Derivables really do make it trivial to maintain consistent (i.e. sense-making) state at all times without requiring that it be kept all in one place. This is a huge win for those of us who develop complex systems with lots of moving parts. | ||
This library satisfies the notion that **changes in state should not cause state changes**, i.e. if the value of state A depends on the value of state B, updates to B should atomically include updates to A—*they should be the same update*. We don't seem to have a handle on this issue, and it causes serious mess in our brains and code. | ||
Derivables clean that mess up by enabling you to make elegant declarative statements about how your bits of state are related. Then, when you update any bits of 'root' state, clever computer-sciency stuff happens in order to keep everything—*every goshdarn thing*—consistent 100% of the time. | ||
There are two types of Derivable: | ||
- **Atoms** are simple references to immutable values. | ||
- **Derivations** represent pure transformation of values held in atoms. | ||
- **Atoms** are simple references to immutable values. They are the roots; the ground truth from which all else is derived. | ||
- **Derivations** represent pure (as in function) transformation of values held in atoms. | ||
Changes in atoms or derivations can be monitored by **Reactions** which do not encapsulate values and exist | ||
solely for side-effects and resource management. | ||
Changes in atoms or derivations can be monitored by **Reactors**, which do not encapsulate values and exist solely for executing side-effects in reaction to state changes. Reactors can also be stopped and restarted when appropriate, and offer lifecycle hooks for the sake of resource management. | ||
@@ -68,7 +69,6 @@ Let's have a look at a tiny example app which greets the user: | ||
// set up a side-effecting reaction to print the message | ||
// set up a Reactor to print the message every time it changes | ||
message.react(msg => console.log(msg)); | ||
// $> Hello, World! | ||
// reactions are automatically re-run when their inputs change | ||
countryCode.set("de"); | ||
@@ -97,5 +97,5 @@ // $> Hallo, World! | ||
The other thing which truly sets derivations apart is that they are *totally lazy*. Like values in Haskell they are computed just in time—on demand. This is another huge win because: | ||
The other thing which truly sets derivations apart is that they are *totally lazy*. Like values in Haskell they are computed just-in-time, i.e. on demand. This is another huge win because: | ||
- It decouples the computational complexity of updating atoms with that of computing their derivations. Derivations are only re-computed at atom-change time if they (the derivations) are actually used by an affected reaction. So, for example, you can declare an eternal relationship between *n* and *n*<sup>2</sup> without needing to fear the cost of re-computing *n*<sup>2</sup> every time *n* changes. That fear is transferred to whoever decides that they want to know the value of *n*<sup>2</sup> at all times, which is just how it should be. | ||
- It decouples the computational complexity of updating atoms with that of computing their derivations. Derivations are only re-computed at atom-change time if they (the derivations) are actually used by an affected reactor. So, for example, you can declare an eternal relationship between *n* and *n*<sup>2</sup> without needing to fear the cost of re-computing *n*<sup>2</sup> every time *n* changes. That fear is transferred to whoever decides that they want to know the value of *n*<sup>2</sup> at all times, which is just how it should be. | ||
- It allows derivations to be automatically garbage collected when you don't need them any more, just like any other object. This is simple to the max! In fact, you don't need any special knowledge to avoid memory leaks with DerivableJS—it Just Works. | ||
@@ -109,3 +109,3 @@ - It permits true short-circuiting boolean logic in derivation structures, which turns out to be extraordinarily practical. | ||
* When an atom is changed, its entire derivation graph is traversed and 'marked'. All active dependent reactions are then gently prodded and told to decide whether they need to re-run themselves. This amounts to an additional whole-graph traversal in the worst case. The worst case also happens to be the common case :( | ||
* When an atom is changed, its entire derivation graph is traversed and 'marked'. All active reactors found in the graph are then gently prodded and told to decide whether they need to re-run themselves. This amounts to an additional whole-graph traversal in the worst case. The worst case also happens to be the common case :( | ||
* The sweep phase involves yet another probably-whole-graph traversal. | ||
@@ -162,5 +162,5 @@ | ||
DerivableJS's API will be unstable until version 1.0.0 is released. This will happen on or before January 1st 2016, whereafter the project will use [Semantic Versioning](http://semver.org/). | ||
DerivableJS's API will be unstable until version 1.0.0 is released, whereafter the project will use [Semantic Versioning](http://semver.org/). | ||
The purpose for this delay is to gather [suggestions and feedback](#contributing) from the community to help shape the core API, but it's a fairly small library so hopefully these things won't take too long. | ||
I plan to wait for the project to pick up a bit more steam so I can get serious community feedback before pumping out a 1.0.0 release. This is to allow for breaking changes if the need arises. | ||
@@ -170,3 +170,3 @@ ## Future Work | ||
1. <s>Shrink the code base. It is currently 5.4k minified and gzipped, but I didn't write the code with size in mind so I think it can get much smaller.</s> now about 3.6k, but could probably get smaller still | ||
1. Dynamic graph optimization. e.g. collapsing derivation branches of frequently-executed reactions into one derivation, maybe trying to align all the data in memory somehow. This would be similar to JIT tracing sans optimization, and could make enormous derivation graphs more feasible (i.e. change propagation could become linear in the number of reactions rather than linear in the number of derivation nodes. It wouldn't work with parent inference though; you'd have to write derivations in the `x.derive((x, y, z) => ..., y, z)` or `derive(x, (x, y, z) => ..., y z)` fashions. So do that if you want to get ahead of the curve! | ||
1. Dynamic graph optimization. e.g. collapsing derivation branches of frequently-executed reactions into one derivation, maybe trying to align all the data in memory somehow. This would be similar to JIT tracing sans optimization, and could make enormous derivation graphs more feasible (i.e. change propagation could become linear in the number of reactors rather than linear in the number of derivation nodes. It wouldn't work with parent inference though; you'd have to write derivations in the `x.derive((x, y, z) => ..., y, z)` or `derive(x, (x, y, z) => ..., y z)` fashions. So do that if you want to get ahead of the curve! | ||
2. Investigate whether asynchronous transactions are possible, or indeed desirable. | ||
@@ -173,0 +173,0 @@ 3. Investigate debugging support. One idea is to instantiate an error A for every derivation and wrap the derivation function in some function which catches other errors but throws A so you get a stack trace pointing to where the derivation was defined. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
256943
1172