Skip to content

Commit

Permalink
perf: split functions into separate modules
Browse files Browse the repository at this point in the history
  • Loading branch information
derrickbeining committed Dec 11, 2018
1 parent 39bf743 commit 56ffae2
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 215 deletions.
2 changes: 1 addition & 1 deletion docs/assets/js/search.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/classes/atom.html
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ <h3><span class="tsd-flag ts-flagStatic">Static</span> of</h3>
<li class="tsd-description">
<aside class="tsd-sources">
<ul>
<li>Defined in atom.ts:50</li>
<li>Defined in atom.ts:29</li>
</ul>
</aside>
<div class="tsd-comment tsd-typography">
Expand Down
11 changes: 6 additions & 5 deletions docs/globals.html
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ <h3>deref</h3>
<li class="tsd-description">
<aside class="tsd-sources">
<ul>
<li>Defined in atom.ts:104</li>
<li>Defined in deref.ts:22</li>
</ul>
</aside>
<div class="tsd-comment tsd-typography">
Expand Down Expand Up @@ -196,7 +196,7 @@ <h3>get<wbr>Validator</h3>
<li class="tsd-description">
<aside class="tsd-sources">
<ul>
<li>Defined in atom.ts:212</li>
<li>Defined in getValidator.ts:24</li>
</ul>
</aside>
<div class="tsd-comment tsd-typography">
Expand Down Expand Up @@ -244,7 +244,7 @@ <h3>set</h3>
<li class="tsd-description">
<aside class="tsd-sources">
<ul>
<li>Defined in atom.ts:174</li>
<li>Defined in set.ts:27</li>
</ul>
</aside>
<div class="tsd-comment tsd-typography">
Expand Down Expand Up @@ -302,7 +302,7 @@ <h3>set<wbr>Validator</h3>
<li class="tsd-description">
<aside class="tsd-sources">
<ul>
<li>Defined in atom.ts:244</li>
<li>Defined in setValidator.ts:30</li>
</ul>
</aside>
<div class="tsd-comment tsd-typography">
Expand All @@ -317,6 +317,7 @@ <h3>set<wbr>Validator</h3>
<dt>example</dt>
<dd><pre><code class="language-js">
<span class="hljs-keyword">import</span> {Atom, deref, setValidator, set} <span class="hljs-keyword">from</span> <span class="hljs-string">'@libre/atom'</span>
<span class="hljs-keyword">import</span> { _setValidator } <span class="hljs-keyword">from</span> <span class="hljs-string">'./internal-state'</span>;

<span class="hljs-keyword">const</span> atom = Atom.of({ <span class="hljs-attr">count</span>: <span class="hljs-number">0</span> }, {<span class="hljs-attr">validator</span>: <span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> isNumber(state.count) })
setValidator(atom, (state) =&gt; isOdd(state.count)) <span class="hljs-comment">// Error; new validator rejected</span>
Expand Down Expand Up @@ -359,7 +360,7 @@ <h3>swap</h3>
<li class="tsd-description">
<aside class="tsd-sources">
<ul>
<li>Defined in atom.ts:131</li>
<li>Defined in swap.ts:25</li>
</ul>
</aside>
<div class="tsd-comment tsd-typography">
Expand Down
207 changes: 7 additions & 200 deletions src/atom.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,6 @@
import * as ErrorMsgs from "./error-messages";
import { _getState, _setState, _setValidator, _useNextAtomId } from "./internal-state";
import { AtomConstructorOptions, DeepImmutable } from "./internal-types";
import { prettyPrint, throwIfNotAtom } from "./utils";
// ------------------------------------------------------------------------------------------ //
// ---------------------------------- INTERNAL STATE ---------------------------------------- //
// ------------------------------------------------------------------------------------------ //

let nextAtomUid = 0;

const stateByAtomId: Record<number, DeepImmutable<any>> = Object.create(null);
const validatorByAtomId: Record<number, NonNullable<AtomConstructorOptions<any>["validator"]>> = Object.create(null);

/** @ignore */
export function getState<S>(atom: Atom<S>): DeepImmutable<S> {
return stateByAtomId[atom["$$id"]];
}

// ------------------------------------------------------------------------------------------ //
// -------------------------------------- PUBLIC API ---------------------------------------- //
// ------------------------------------------------------------------------------------------ //
import { _prettyPrint } from "./utils";

/**
* A data structure useful for providing a controlled, predictable mechanism for mutability.
Expand All @@ -27,10 +10,6 @@ export function getState<S>(atom: Atom<S>): DeepImmutable<S> {
*
*/

//
// ======================================= ATOM ==============================================
//

export class Atom<S = any> {
/**
* Constructs a new instance of [[Atom]] with its internal state
Expand Down Expand Up @@ -58,197 +37,25 @@ const a3 = Atom.of({ count: 0 })
private constructor(state: S, { validator }: AtomConstructorOptions<S> = {}) {
validator = validator || (() => true);
if (!validator(state as DeepImmutable<S>)) {
const errMsg = `Atom initialized with invalid state:\n\n${prettyPrint(
const errMsg = `Atom initialized with invalid state:\n\n${_prettyPrint(
state
)}\n\naccording to validator function:\n${validator}\n\n`;
const err = Error(errMsg);
err.name = "AtomInvalidStateError";

throw err;
}
Object.defineProperty(this, "$$id", { value: nextAtomUid++ });
stateByAtomId[this["$$id"]] = state;
validatorByAtomId[this["$$id"]] = validator;
Object.defineProperty(this, "$$id", { value: _useNextAtomId() });
_setState(this, state);
_setValidator(this, validator);
return this;
}
/** @ignore */
public toString(): string {
return `Atom<${prettyPrint(getState(this))}>`;
return `Atom<${_prettyPrint(_getState(this))}>`;
}
/** @ignore */
public inspect(): string {
return this.toString();
}
}

//
// ======================================= DEREF ==============================================
//

/**
* Dereferences (i.e. "*reads*") the current state of an [[Atom]]. The dereferenced value
* should ___not___ be mutated.
*
* @param <S> the type of `atom`'s inner state
*
* @example
```js
import {Atom, deref} from '@libre/atom'
const stateAtom = Atom.of({ count: 0 })
deref(stateAtom) // => { count: 0 }
```
*/
export function deref<S>(atom: Atom<S>): DeepImmutable<S> {
throwIfNotAtom(atom);
return getState(atom);
}

//
// ======================================= SWAP ==============================================
//
/**
* Swaps `atom`'s state with the value returned from applying `updateFn` to `atom`'s
* current state. `updateFn` should be a pure function and ___not___ mutate `state`.
*
* @param <S> the type of `atom`'s inner state
* @param atom an instance of [[Atom]]
* @param updateFn a pure function that takes the current state and returns the next state; the next state should be of the same type/interface as the current state;
*
* @example
* ```jsx
*
*import {Atom, swap} from '@libre/atom'
*
*const stateAtom = Atom.of({ count: 0 })
*const increment = () => swap(stateAtom, (state) => ({
* count: state.count + 1
*}));
* ```
*/
export function swap<S>(atom: Atom<S>, updateFn: (state: DeepImmutable<S>) => S): void {
throwIfNotAtom(atom);
const nextState = updateFn(getState(atom));
const validator = getValidator(atom);
const didValidate = validator(nextState);
if (!didValidate) {
const errMsg = `swap updateFn\n${updateFn}\n\nattempted to swap the state of\n\n${atom}\n\nwith:\n\n${prettyPrint(
nextState
)}\n\nbut it did not pass validator:\n${validator}\n\n`;
const err = Error(errMsg);
err.name = "AtomInvalidStateError";

throw err;
} else {
stateByAtomId[atom["$$id"]] = nextState;
}
}

//
// ======================================= SET ==============================================
//

/**
* Sets `atom`s state to `nextState`.
*
* It is equivalent to `swap(atom, () => newState)`.
*
* @param <S> the type of `atom`'s inner state
* @param atom an instance of [[Atom]]
* @param nextState the value to which to set the state; it should be the same type/interface as current state
*
* @example
```js
import {Atom, deref, set} from '@libre/atom'
const atom = Atom.of({ count: 0 })
set(atom, { count: 100 })
deref(atom) // => { count: 100 }
```
*/

export function set<S>(atom: Atom<S>, nextState: S): void {
throwIfNotAtom(atom);
const validator = getValidator(atom);
const didValidate = validator(nextState);
if (!didValidate) {
const errMsg = `Attempted to set the state of\n\n${atom}\n\nwith:\n\n${prettyPrint(
nextState
)}\n\nbut it did not pass validator:\n${validator}\n\n`;
const err = Error(errMsg);
err.name = "AtomInvalidStateError";

throw err;
} else {
stateByAtomId[atom["$$id"]] = nextState;
}
}

//
// ======================================= GETVALIDATOR ==============================================
//

/**
* Gets `atom`'s validator function
*
* @param <S> the type of `atom`'s inner state
*
* @example
```js
import {Atom, deref, getValidator, swap} from '@libre/atom'
const atom = Atom.of({ count: 0 }, { validator: (state) => isEven(state.count) })
const validator = getValidator(atom)
validator({ count: 3 }) // => false
validator({ count: 2 }) // => true
```
*/

export function getValidator<S>(atom: Atom<S>): NonNullable<AtomConstructorOptions<any>["validator"]> {
throwIfNotAtom(atom);
return validatorByAtomId[atom["$$id"]];
}

//
// ======================================= SETVALIDATOR ==============================================
//

/**
* Sets the `validator` for `atom`. `validator` must be a pure function of one argument,
* which will be passed the intended new state on any state change. If the new state is
* unacceptable, `validator` should return false or throw an exception. If the current state
* is not acceptable to the new validator, an exception will be thrown and the validator will
* not be changed.
*
* @param <S> the type of `atom`'s inner state
*
* @example
```js
import {Atom, deref, setValidator, set} from '@libre/atom'
const atom = Atom.of({ count: 0 }, {validator: (state) => isNumber(state.count) })
setValidator(atom, (state) => isOdd(state.count)) // Error; new validator rejected
set(atom, {count: "not number"}) // Error; new state not set
setValidator(atom, (state) => isEven(state.count)) // All good
set(atom, {count: 2}) // All good
```
*/

export function setValidator<S>(atom: Atom<S>, validator: NonNullable<AtomConstructorOptions<any>["validator"]>): void {
throwIfNotAtom(atom);
if (!validator(getState(atom))) {
const errMsg = `Could not set validator on\n\n${atom}\n\nbecause current state would be invalid according to new validator:\n${validator}\n\n`;
const err = Error(errMsg);
err.name = "AtomInvalidStateError";
throw err;
} else {
validatorByAtomId[atom["$$id"]] = validator;
}
}
25 changes: 25 additions & 0 deletions src/deref.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Atom } from "./atom";
import { _getState } from "./internal-state";
import { DeepImmutable } from "./internal-types";
import { _throwIfNotAtom } from "./utils";

/**
* Dereferences (i.e. "*reads*") the current state of an [[Atom]]. The dereferenced value
* should ___not___ be mutated.
*
* @param <S> the type of `atom`'s inner state
*
* @example
```js
import {Atom, deref} from '@libre/atom'
const stateAtom = Atom.of({ count: 0 })
deref(stateAtom) // => { count: 0 }
```
*/
export function deref<S>(atom: Atom<S>): DeepImmutable<S> {
_throwIfNotAtom(atom);
return _getState(atom);
}
27 changes: 27 additions & 0 deletions src/getValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Atom } from "./atom";
import { _getValidator } from "./internal-state";
import { AtomConstructorOptions } from "./internal-types";

import { _throwIfNotAtom } from "./utils";

/**
* Gets `atom`'s validator function
*
* @param <S> the type of `atom`'s inner state
*
* @example
```js
import {Atom, deref, getValidator, swap} from '@libre/atom'
const atom = Atom.of({ count: 0 }, { validator: (state) => isEven(state.count) })
const validator = getValidator(atom)
validator({ count: 3 }) // => false
validator({ count: 2 }) // => true
```
*/

export function getValidator<S>(atom: Atom<S>): NonNullable<AtomConstructorOptions<any>["validator"]> {
_throwIfNotAtom(atom);
return _getValidator(atom);
}
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export { Atom, deref, swap, set, setValidator, getValidator } from "./atom";
export { Atom } from "./atom";
export { AtomState } from "./internal-types";
export { deref } from "./deref";
export { getValidator } from "./getValidator";
export { set } from "./set";
export { setValidator } from "./setValidator";
export { swap } from "./swap";
Loading

0 comments on commit 56ffae2

Please sign in to comment.