Unify Derivation and Prism 7/n
This commit is contained in:
parent
859cb40e0f
commit
acf34d393d
25 changed files with 134 additions and 141 deletions
|
@ -20,7 +20,7 @@ enum ValueTypes {
|
|||
}
|
||||
|
||||
/**
|
||||
* Interface for objects that can provide a derivation at a certain path.
|
||||
* Interface for objects that can provide a prism at a certain path.
|
||||
*/
|
||||
export interface IdentityPrismProvider {
|
||||
/**
|
||||
|
@ -30,9 +30,9 @@ export interface IdentityPrismProvider {
|
|||
*/
|
||||
readonly $$isIdentityPrismProvider: true
|
||||
/**
|
||||
* Returns a derivation of the value at the provided path.
|
||||
* Returns a prism of the value at the provided path.
|
||||
*
|
||||
* @param path - The path to create the derivation at.
|
||||
* @param path - The path to create the prism at.
|
||||
*/
|
||||
getIdentityPrism(path: Array<string | number>): Prism<unknown>
|
||||
}
|
||||
|
@ -240,9 +240,9 @@ export default class Atom<State extends {}> implements IdentityPrismProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a new derivation of the value at the provided path.
|
||||
* Returns a new prism of the value at the provided path.
|
||||
*
|
||||
* @param path - The path to create the derivation at.
|
||||
* @param path - The path to create the prism at.
|
||||
*/
|
||||
getIdentityPrism(path: Array<string | number>): Prism<unknown> {
|
||||
const subscribe = (listener: (val: unknown) => void) =>
|
||||
|
@ -259,18 +259,18 @@ export default class Atom<State extends {}> implements IdentityPrismProvider {
|
|||
const identifyPrismWeakMap = new WeakMap<{}, Prism<unknown>>()
|
||||
|
||||
/**
|
||||
* Returns a derivation of the value at the provided pointer. Derivations are
|
||||
* Returns a prism of the value at the provided pointer. Prisms are
|
||||
* cached per pointer.
|
||||
*
|
||||
* @param pointer - The pointer to return the derivation at.
|
||||
* @param pointer - The pointer to return the prism at.
|
||||
*/
|
||||
export const pointerToPrism = <P extends PointerType<$IntentionalAny>>(
|
||||
pointer: P,
|
||||
): Prism<P extends PointerType<infer T> ? T : void> => {
|
||||
const meta = getPointerMeta(pointer)
|
||||
|
||||
let derivation = identifyPrismWeakMap.get(meta)
|
||||
if (!derivation) {
|
||||
let prismInstance = identifyPrismWeakMap.get(meta)
|
||||
if (!prismInstance) {
|
||||
const root = meta.root
|
||||
if (!isIdentityPrismProvider(root)) {
|
||||
throw new Error(
|
||||
|
@ -278,10 +278,10 @@ export const pointerToPrism = <P extends PointerType<$IntentionalAny>>(
|
|||
)
|
||||
}
|
||||
const {path} = meta
|
||||
derivation = root.getIdentityPrism(path)
|
||||
identifyPrismWeakMap.set(meta, derivation)
|
||||
prismInstance = root.getIdentityPrism(path)
|
||||
identifyPrismWeakMap.set(meta, prismInstance)
|
||||
}
|
||||
return derivation as $IntentionalAny
|
||||
return prismInstance as $IntentionalAny
|
||||
}
|
||||
|
||||
function isIdentityPrismProvider(val: unknown): val is IdentityPrismProvider {
|
||||
|
@ -294,10 +294,10 @@ function isIdentityPrismProvider(val: unknown): val is IdentityPrismProvider {
|
|||
|
||||
/**
|
||||
* Convenience function that returns a plain value from its argument, whether it
|
||||
* is a pointer, a derivation or a plain value itself.
|
||||
* is a pointer, a prism or a plain value itself.
|
||||
*
|
||||
* @remarks
|
||||
* For pointers, the value is returned by first creating a derivation, so it is
|
||||
* For pointers, the value is returned by first creating a prism, so it is
|
||||
* reactive e.g. when used in a `prism`.
|
||||
*
|
||||
* @param input - The argument to return a value from.
|
||||
|
|
|
@ -17,24 +17,23 @@ export interface IBox<V> {
|
|||
* Gets the value of the Box.
|
||||
*
|
||||
* @remarks
|
||||
* Usages of `get()` aren't tracked, they are only for retrieving the value. To track changes, you need to
|
||||
* create a derivation.
|
||||
* Usages of `get()` aren't tracked, they are only for retrieving the value. To track changes, you need use a prism.
|
||||
*
|
||||
* @see derivation
|
||||
* @see prism
|
||||
*/
|
||||
get(): V
|
||||
|
||||
/**
|
||||
* Creates a derivation of the Box that you can use to track changes to it.
|
||||
* Returns a prism of the Box that you can use to track changes to it.
|
||||
*/
|
||||
derivation: Prism<V>
|
||||
prism: Prism<V>
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a single value.
|
||||
*
|
||||
* @remarks
|
||||
* Derivations created with {@link Box.derivation} update based on strict equality (`===`) of the old value and the new one.
|
||||
* Derivations created with {@link Box.prism} update based on strict equality (`===`) of the old value and the new one.
|
||||
* This also means that property-changes of objects won't be tracked, and that for objects, updates will trigger on changes of
|
||||
* reference even if the objects are structurally equal.
|
||||
*/
|
||||
|
@ -74,18 +73,18 @@ export default class Box<V> implements IBox<V> {
|
|||
* Gets the value of the Box.
|
||||
*
|
||||
* Note: usages of `get()` aren't tracked, they are only for retrieving the value. To track changes, you need to
|
||||
* create a derivation.
|
||||
* use a prism.
|
||||
*
|
||||
* @see Box.derivation
|
||||
* @see Box.prism
|
||||
*/
|
||||
get() {
|
||||
return this._value
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a derivation of the Box that you can use to track changes to it.
|
||||
* Returns a prism of the Box that you can use to track changes to it.
|
||||
*/
|
||||
get derivation() {
|
||||
get prism() {
|
||||
return this._publicDerivation
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,13 +44,13 @@ export default class PointerProxy<O extends {}>
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a derivation of the value at the provided sub-path of the proxied pointer.
|
||||
* Returns a prism of the value at the provided sub-path of the proxied pointer.
|
||||
*
|
||||
* @param path - The path to create the derivation at.
|
||||
* @param path - The path to create the prism at.
|
||||
*/
|
||||
getIdentityPrism(path: Array<string | number>) {
|
||||
return prism(() => {
|
||||
const currentPointer = this._currentPointerBox.derivation.getValue()
|
||||
const currentPointer = this._currentPointerBox.prism.getValue()
|
||||
const subPointer = path.reduce(
|
||||
(pointerSoFar, pathItem) => (pointerSoFar as $IntentionalAny)[pathItem],
|
||||
currentPointer,
|
||||
|
|
|
@ -4,16 +4,16 @@ import type {$IntentionalAny, VoidFn} from '../types'
|
|||
type IDependent = (msgComingFrom: Prism<$IntentionalAny>) => void
|
||||
|
||||
/**
|
||||
* Common interface for derivations.
|
||||
* Common interface for prisms.
|
||||
*/
|
||||
export interface Prism<V> {
|
||||
/**
|
||||
* Whether the object is a derivation.
|
||||
* Whether the object is a prism.
|
||||
*/
|
||||
isPrism: true
|
||||
|
||||
/**
|
||||
* Whether the derivation is hot.
|
||||
* Whether the prism is hot.
|
||||
*/
|
||||
isHot: boolean
|
||||
|
||||
|
@ -29,14 +29,14 @@ export interface Prism<V> {
|
|||
onStale(cb: () => void): VoidFn
|
||||
|
||||
/**
|
||||
* Keep the derivation hot, even if there are no tappers (subscribers).
|
||||
* Keep the prism hot, even if there are no tappers (subscribers).
|
||||
*/
|
||||
keepHot(): VoidFn
|
||||
|
||||
/**
|
||||
* Add a derivation as a dependent of this derivation.
|
||||
* Add a prism as a dependent of this prism.
|
||||
*
|
||||
* @param d - The derivation to be made a dependent of this derivation.
|
||||
* @param d - The prism to be made a dependent of this prism.
|
||||
*
|
||||
* @see _removeDependent
|
||||
*
|
||||
|
@ -45,9 +45,9 @@ export interface Prism<V> {
|
|||
_addDependent(d: IDependent): void
|
||||
|
||||
/**
|
||||
* Remove a derivation as a dependent of this derivation.
|
||||
* Remove a prism as a dependent of this prism.
|
||||
*
|
||||
* @param d - The derivation to be removed from as a dependent of this derivation.
|
||||
* @param d - The prism to be removed from as a dependent of this prism.
|
||||
*
|
||||
* @see _addDependent
|
||||
* @internal
|
||||
|
@ -55,13 +55,13 @@ export interface Prism<V> {
|
|||
_removeDependent(d: IDependent): void
|
||||
|
||||
/**
|
||||
* Gets the current value of the derivation. If the value is stale, it causes the derivation to freshen.
|
||||
* Gets the current value of the prism. If the value is stale, it causes the prism to freshen.
|
||||
*/
|
||||
getValue(): V
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether `d` is a derivation.
|
||||
* Returns whether `d` is a prism.
|
||||
*/
|
||||
export function isPrism(d: any): d is Prism<unknown> {
|
||||
return d && d.isPrism && d.isPrism === true
|
||||
|
|
|
@ -93,7 +93,7 @@ describe('prism', () => {
|
|||
|
||||
const a = new Atom({letter: 'a'})
|
||||
|
||||
const derivation = prism(() => {
|
||||
const prsm = prism(() => {
|
||||
const n = val(a.pointer.letter)
|
||||
const iterationAtTimeOfCall = iteration
|
||||
sequence.push({derivationCall: iterationAtTimeOfCall})
|
||||
|
@ -112,7 +112,7 @@ describe('prism', () => {
|
|||
return n
|
||||
})
|
||||
|
||||
const untap = derivation.onChange(ticker, (change) => {
|
||||
const untap = prsm.onChange(ticker, (change) => {
|
||||
sequence.push({change})
|
||||
})
|
||||
|
||||
|
@ -153,7 +153,7 @@ describe('prism', () => {
|
|||
|
||||
const a = new Atom({letter: 'a'})
|
||||
|
||||
const derivation = prism(() => {
|
||||
const prsm = prism(() => {
|
||||
const n = val(a.pointer.letter)
|
||||
const iterationAtTimeOfCall = iteration
|
||||
sequence.push({derivationCall: iterationAtTimeOfCall})
|
||||
|
@ -172,7 +172,7 @@ describe('prism', () => {
|
|||
return n
|
||||
})
|
||||
|
||||
const untap = derivation.onChange(ticker, (change) => {
|
||||
const untap = prsm.onChange(ticker, (change) => {
|
||||
sequence.push({change})
|
||||
})
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ class HotHandle<V> {
|
|||
protected _lastValue: undefined | V = undefined
|
||||
|
||||
/**
|
||||
* If true, the derivation is stale even though its dependencies aren't
|
||||
* If true, the prism is stale even though its dependencies aren't
|
||||
* marked as such. This is used by `prism.source()` and `prism.state()`
|
||||
* to mark the prism as stale.
|
||||
*/
|
||||
|
@ -200,7 +200,7 @@ const emptyObject = {}
|
|||
|
||||
class PrismDerivation<V> implements Prism<V> {
|
||||
/**
|
||||
* Whether the object is a derivation.
|
||||
* Whether the object is a prism.
|
||||
*/
|
||||
readonly isPrism: true = true
|
||||
|
||||
|
@ -214,7 +214,7 @@ class PrismDerivation<V> implements Prism<V> {
|
|||
constructor(private readonly _fn: () => V) {}
|
||||
|
||||
/**
|
||||
* Whether the derivation is hot.
|
||||
* Whether the prism is hot.
|
||||
*/
|
||||
get isHot(): boolean {
|
||||
return this._state.hot
|
||||
|
@ -266,16 +266,16 @@ class PrismDerivation<V> implements Prism<V> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Keep the derivation hot, even if there are no tappers (subscribers).
|
||||
* Keep the prism hot, even if there are no tappers (subscribers).
|
||||
*/
|
||||
keepHot() {
|
||||
return this.onStale(() => {})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a derivation as a dependent of this derivation.
|
||||
* Add a prism as a dependent of this prism.
|
||||
*
|
||||
* @param d - The derivation to be made a dependent of this derivation.
|
||||
* @param d - The prism to be made a dependent of this prism.
|
||||
*
|
||||
* @see _removeDependent
|
||||
*/
|
||||
|
@ -295,9 +295,9 @@ class PrismDerivation<V> implements Prism<V> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Remove a derivation as a dependent of this derivation.
|
||||
* Remove a prism as a dependent of this prism.
|
||||
*
|
||||
* @param d - The derivation to be removed from as a dependent of this derivation.
|
||||
* @param d - The prism to be removed from as a dependent of this prism.
|
||||
*
|
||||
* @see _addDependent
|
||||
*/
|
||||
|
@ -315,23 +315,23 @@ class PrismDerivation<V> implements Prism<V> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of the derivation. If the value is stale, it causes the derivation to freshen.
|
||||
* Gets the current value of the prism. If the value is stale, it causes the prism to freshen.
|
||||
*/
|
||||
getValue(): V {
|
||||
/**
|
||||
* TODO We should prevent (or warn about) a common mistake users make, which is reading the value of
|
||||
* a derivation in the body of a react component (e.g. `der.getValue()` (often via `val()`) instead of `useVal()`
|
||||
* a prism in the body of a react component (e.g. `der.getValue()` (often via `val()`) instead of `useVal()`
|
||||
* or `uesPrism()`).
|
||||
*
|
||||
* Although that's the most common example of this mistake, you can also find it outside of react components.
|
||||
* Basically the user runs `der.getValue()` assuming the read is detected by a wrapping prism when it's not.
|
||||
*
|
||||
* Sometiems the derivation isn't even hot when the user assumes it is.
|
||||
* Sometiems the prism isn't even hot when the user assumes it is.
|
||||
*
|
||||
* We can fix this type of mistake by:
|
||||
* 1. Warning the user when they call `getValue()` on a cold derivation.
|
||||
* 2. Warning the user about calling `getValue()` on a hot-but-stale derivation
|
||||
* if `getValue()` isn't called by a known mechanism like a `DerivationEmitter`.
|
||||
* 1. Warning the user when they call `getValue()` on a cold prism.
|
||||
* 2. Warning the user about calling `getValue()` on a hot-but-stale prism
|
||||
* if `getValue()` isn't called by a known mechanism like a `PrismEmitter`.
|
||||
*
|
||||
* Design constraints:
|
||||
* - This fix should not have a perf-penalty in production. Perhaps use a global flag + `process.env.NODE_ENV !== 'production'`
|
||||
|
@ -616,7 +616,7 @@ function memo<T>(
|
|||
* ```ts
|
||||
* import {prism} from 'dataverse'
|
||||
*
|
||||
* // This derivation holds the current mouse position and updates when the mouse moves
|
||||
* // This prism holds the current mouse position and updates when the mouse moves
|
||||
* const mousePositionD = prism(() => {
|
||||
* const [pos, setPos] = prism.state<[x: number, y: number]>('pos', [0, 0])
|
||||
*
|
||||
|
@ -736,10 +736,10 @@ type IPrismFn = {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a derivation from the passed function that adds all derivations referenced
|
||||
* Creates a prism from the passed function that adds all prisms referenced
|
||||
* in it as dependencies, and reruns the function when these change.
|
||||
*
|
||||
* @param fn - The function to rerun when the derivations referenced in it change.
|
||||
* @param fn - The function to rerun when the prisms referenced in it change.
|
||||
*/
|
||||
const prism: IPrismFn = (fn) => {
|
||||
return new PrismDerivation(fn)
|
||||
|
|
|
@ -153,7 +153,7 @@ export const getPointerParts = <_>(
|
|||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Here, sum is a derivation that updates whenever the a or b prop of someAtom does.
|
||||
* // Here, sum is a prism that updates whenever the a or b prop of someAtom does.
|
||||
* const sum = prism(() => {
|
||||
* return val(pointer({root: someAtom, path: ['a']})) + val(pointer({root: someAtom, path: ['b']}));
|
||||
* });
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue