Unify Derivation and Prism 7/n

This commit is contained in:
Aria Minaei 2022-12-01 14:41:46 +01:00
parent 859cb40e0f
commit acf34d393d
25 changed files with 134 additions and 141 deletions

View file

@ -175,8 +175,8 @@ export const valueDerivation = <P extends PointerType<$IntentionalAny>>(
): IDerivation<P extends PointerType<infer T> ? T : void> => { ): IDerivation<P extends PointerType<infer T> ? T : void> => {
const meta = getPointerMeta(pointer) const meta = getPointerMeta(pointer)
let derivation = identityDerivationWeakMap.get(meta) let pr = identityDerivationWeakMap.get(meta)
if (!derivation) { if (!pr) {
const root = meta.root const root = meta.root
if (!(root instanceof Atom)) { if (!(root instanceof Atom)) {
throw new Error( throw new Error(
@ -184,13 +184,13 @@ export const valueDerivation = <P extends PointerType<$IntentionalAny>>(
) )
} }
const {path} = meta const {path} = meta
derivation = new DerivationFromSource<$IntentionalAny>( pr = new DerivationFromSource<$IntentionalAny>(
(listener) => root.onPathValueChange(path, listener), (listener) => root.onPathValueChange(path, listener),
() => root.getIn(path), () => root.getIn(path),
) )
identityDerivationWeakMap.set(meta, derivation) identityDerivationWeakMap.set(meta, pr)
} }
return derivation as $IntentionalAny return pr as $IntentionalAny
} }
export const val = <P>( export const val = <P>(

View file

@ -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 { export interface IdentityPrismProvider {
/** /**
@ -30,9 +30,9 @@ export interface IdentityPrismProvider {
*/ */
readonly $$isIdentityPrismProvider: true 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> 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> { getIdentityPrism(path: Array<string | number>): Prism<unknown> {
const subscribe = (listener: (val: unknown) => void) => const subscribe = (listener: (val: unknown) => void) =>
@ -259,18 +259,18 @@ export default class Atom<State extends {}> implements IdentityPrismProvider {
const identifyPrismWeakMap = new WeakMap<{}, Prism<unknown>>() 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. * 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>>( export const pointerToPrism = <P extends PointerType<$IntentionalAny>>(
pointer: P, pointer: P,
): Prism<P extends PointerType<infer T> ? T : void> => { ): Prism<P extends PointerType<infer T> ? T : void> => {
const meta = getPointerMeta(pointer) const meta = getPointerMeta(pointer)
let derivation = identifyPrismWeakMap.get(meta) let prismInstance = identifyPrismWeakMap.get(meta)
if (!derivation) { if (!prismInstance) {
const root = meta.root const root = meta.root
if (!isIdentityPrismProvider(root)) { if (!isIdentityPrismProvider(root)) {
throw new Error( throw new Error(
@ -278,10 +278,10 @@ export const pointerToPrism = <P extends PointerType<$IntentionalAny>>(
) )
} }
const {path} = meta const {path} = meta
derivation = root.getIdentityPrism(path) prismInstance = root.getIdentityPrism(path)
identifyPrismWeakMap.set(meta, derivation) identifyPrismWeakMap.set(meta, prismInstance)
} }
return derivation as $IntentionalAny return prismInstance as $IntentionalAny
} }
function isIdentityPrismProvider(val: unknown): val is IdentityPrismProvider { 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 * 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 * @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`. * reactive e.g. when used in a `prism`.
* *
* @param input - The argument to return a value from. * @param input - The argument to return a value from.

View file

@ -17,24 +17,23 @@ export interface IBox<V> {
* Gets the value of the Box. * Gets the value of the Box.
* *
* @remarks * @remarks
* Usages of `get()` aren't tracked, they are only for retrieving the value. To track changes, you need to * Usages of `get()` aren't tracked, they are only for retrieving the value. To track changes, you need use a prism.
* create a derivation.
* *
* @see derivation * @see prism
*/ */
get(): V 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. * Wraps a single value.
* *
* @remarks * @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 * 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. * 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. * 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 * 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() { get() {
return this._value 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 return this._publicDerivation
} }
} }

View file

@ -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>) { getIdentityPrism(path: Array<string | number>) {
return prism(() => { return prism(() => {
const currentPointer = this._currentPointerBox.derivation.getValue() const currentPointer = this._currentPointerBox.prism.getValue()
const subPointer = path.reduce( const subPointer = path.reduce(
(pointerSoFar, pathItem) => (pointerSoFar as $IntentionalAny)[pathItem], (pointerSoFar, pathItem) => (pointerSoFar as $IntentionalAny)[pathItem],
currentPointer, currentPointer,

View file

@ -4,16 +4,16 @@ import type {$IntentionalAny, VoidFn} from '../types'
type IDependent = (msgComingFrom: Prism<$IntentionalAny>) => void type IDependent = (msgComingFrom: Prism<$IntentionalAny>) => void
/** /**
* Common interface for derivations. * Common interface for prisms.
*/ */
export interface Prism<V> { export interface Prism<V> {
/** /**
* Whether the object is a derivation. * Whether the object is a prism.
*/ */
isPrism: true isPrism: true
/** /**
* Whether the derivation is hot. * Whether the prism is hot.
*/ */
isHot: boolean isHot: boolean
@ -29,14 +29,14 @@ export interface Prism<V> {
onStale(cb: () => void): VoidFn 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 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 * @see _removeDependent
* *
@ -45,9 +45,9 @@ export interface Prism<V> {
_addDependent(d: IDependent): void _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 * @see _addDependent
* @internal * @internal
@ -55,13 +55,13 @@ export interface Prism<V> {
_removeDependent(d: IDependent): void _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 getValue(): V
} }
/** /**
* Returns whether `d` is a derivation. * Returns whether `d` is a prism.
*/ */
export function isPrism(d: any): d is Prism<unknown> { export function isPrism(d: any): d is Prism<unknown> {
return d && d.isPrism && d.isPrism === true return d && d.isPrism && d.isPrism === true

View file

@ -93,7 +93,7 @@ describe('prism', () => {
const a = new Atom({letter: 'a'}) const a = new Atom({letter: 'a'})
const derivation = prism(() => { const prsm = prism(() => {
const n = val(a.pointer.letter) const n = val(a.pointer.letter)
const iterationAtTimeOfCall = iteration const iterationAtTimeOfCall = iteration
sequence.push({derivationCall: iterationAtTimeOfCall}) sequence.push({derivationCall: iterationAtTimeOfCall})
@ -112,7 +112,7 @@ describe('prism', () => {
return n return n
}) })
const untap = derivation.onChange(ticker, (change) => { const untap = prsm.onChange(ticker, (change) => {
sequence.push({change}) sequence.push({change})
}) })
@ -153,7 +153,7 @@ describe('prism', () => {
const a = new Atom({letter: 'a'}) const a = new Atom({letter: 'a'})
const derivation = prism(() => { const prsm = prism(() => {
const n = val(a.pointer.letter) const n = val(a.pointer.letter)
const iterationAtTimeOfCall = iteration const iterationAtTimeOfCall = iteration
sequence.push({derivationCall: iterationAtTimeOfCall}) sequence.push({derivationCall: iterationAtTimeOfCall})
@ -172,7 +172,7 @@ describe('prism', () => {
return n return n
}) })
const untap = derivation.onChange(ticker, (change) => { const untap = prsm.onChange(ticker, (change) => {
sequence.push({change}) sequence.push({change})
}) })

View file

@ -43,7 +43,7 @@ class HotHandle<V> {
protected _lastValue: undefined | V = undefined 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()` * marked as such. This is used by `prism.source()` and `prism.state()`
* to mark the prism as stale. * to mark the prism as stale.
*/ */
@ -200,7 +200,7 @@ const emptyObject = {}
class PrismDerivation<V> implements Prism<V> { class PrismDerivation<V> implements Prism<V> {
/** /**
* Whether the object is a derivation. * Whether the object is a prism.
*/ */
readonly isPrism: true = true readonly isPrism: true = true
@ -214,7 +214,7 @@ class PrismDerivation<V> implements Prism<V> {
constructor(private readonly _fn: () => V) {} constructor(private readonly _fn: () => V) {}
/** /**
* Whether the derivation is hot. * Whether the prism is hot.
*/ */
get isHot(): boolean { get isHot(): boolean {
return this._state.hot 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() { keepHot() {
return this.onStale(() => {}) 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 * @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 * @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 { getValue(): V {
/** /**
* TODO We should prevent (or warn about) a common mistake users make, which is reading the value of * 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()`). * or `uesPrism()`).
* *
* Although that's the most common example of this mistake, you can also find it outside of react components. * 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. * 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: * We can fix this type of mistake by:
* 1. Warning the user when they call `getValue()` on a cold derivation. * 1. Warning the user when they call `getValue()` on a cold prism.
* 2. Warning the user about calling `getValue()` on a hot-but-stale derivation * 2. Warning the user about calling `getValue()` on a hot-but-stale prism
* if `getValue()` isn't called by a known mechanism like a `DerivationEmitter`. * if `getValue()` isn't called by a known mechanism like a `PrismEmitter`.
* *
* Design constraints: * Design constraints:
* - This fix should not have a perf-penalty in production. Perhaps use a global flag + `process.env.NODE_ENV !== 'production'` * - 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 * ```ts
* import {prism} from 'dataverse' * 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 mousePositionD = prism(() => {
* const [pos, setPos] = prism.state<[x: number, y: number]>('pos', [0, 0]) * 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. * 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) => { const prism: IPrismFn = (fn) => {
return new PrismDerivation(fn) return new PrismDerivation(fn)

View file

@ -153,7 +153,7 @@ export const getPointerParts = <_>(
* *
* @example * @example
* ```ts * ```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(() => { * const sum = prism(() => {
* return val(pointer({root: someAtom, path: ['a']})) + val(pointer({root: someAtom, path: ['b']})); * return val(pointer({root: someAtom, path: ['a']})) + val(pointer({root: someAtom, path: ['b']}));
* }); * });

View file

@ -31,7 +31,7 @@ studio.extend({
const untapFn = prism<ToolsetConfig>(() => [ const untapFn = prism<ToolsetConfig>(() => [
{ {
type: 'Switch', type: 'Switch',
value: val(exampleBox.derivation), value: val(exampleBox.prism),
onChange: (value) => exampleBox.set(value), onChange: (value) => exampleBox.set(value),
options: [ options: [
{ {
@ -55,7 +55,7 @@ studio.extend({
}, },
}, },
]) ])
// listen to changes to this derivation using the requestAnimationFrame shared ticker // listen to changes to this prism using the requestAnimationFrame shared ticker
.onChange( .onChange(
Ticker.raf, Ticker.raf,
(value) => { (value) => {

View file

@ -65,16 +65,16 @@ export function usePrism<T>(
boxRef.current.set(fnAsCallback) boxRef.current.set(fnAsCallback)
} }
const derivation = useMemo( const pr = useMemo(
() => () =>
prism(() => { prism(() => {
const fn = boxRef.current.derivation.getValue() const fn = boxRef.current.prism.getValue()
return fn() return fn()
}), }),
[], [],
) )
return useDerivation(derivation, debugLabel) return useDerivation(pr, debugLabel)
} }
export const useVal: typeof val = (p: $IntentionalAny, debugLabel?: string) => { export const useVal: typeof val = (p: $IntentionalAny, debugLabel?: string) => {
@ -88,9 +88,9 @@ export const useVal: typeof val = (p: $IntentionalAny, debugLabel?: string) => {
let lastOrder = 0 let lastOrder = 0
/** /**
* A sorted array of derivations that need to be refreshed. The derivations are sorted * A sorted array of prisms that need to be refreshed. The prisms are sorted
* by their order, which means a parent derivation always gets priority to children * by their order, which means a parent prism always gets priority to children
* and descendents. Ie. we refresh the derivations top to bottom. * and descendents. Ie. we refresh the prisms top to bottom.
*/ */
const queue: QueueItem[] = [] const queue: QueueItem[] = []
const setOfQueuedItems = new Set<QueueItem>() const setOfQueuedItems = new Set<QueueItem>()
@ -99,7 +99,7 @@ type QueueItem<T = unknown> = {
order: number order: number
/** /**
* runUpdate() is the equivalent of a forceUpdate() call. It would only be called * runUpdate() is the equivalent of a forceUpdate() call. It would only be called
* if the value of the inner derivation has _actually_ changed. * if the value of the inner prism has _actually_ changed.
*/ */
runUpdate: VoidFn runUpdate: VoidFn
/** /**
@ -142,11 +142,11 @@ type QueueItem<T = unknown> = {
> >
} }
/** /**
* A reference to the derivation * A reference to the prism
*/ */
der: Prism<T> der: Prism<T>
/** /**
* The last value of this derivation. * The last value of this prism.
*/ */
lastValue: T lastValue: T
/** /**
@ -246,17 +246,17 @@ function queueIfNeeded() {
}) })
} }
/** /**
* A React hook that returns the value of the derivation that it received as the first argument. * A React hook that returns the value of the prism that it received as the first argument.
* It works like an implementation of Dataverse's Ticker, except that it runs the side effects in * It works like an implementation of Dataverse's Ticker, except that it runs the side effects in
* an order where a component's derivation is guaranteed to run before any of its descendents' derivations. * an order where a component's prism is guaranteed to run before any of its descendents' prisms.
* *
* @param der - The derivation * @param der - The prism
* @param debugLabel - The label used by the debugger * @param debugLabel - The label used by the debugger
* *
* @remarks * @remarks
* It looks like this new implementation of useDerivation() manages to: * It looks like this new implementation of usePrism() manages to:
* 1. Not over-calculate the derivations * 1. Not over-calculate the prisms
* 2. Render derivation in ancestor -\> descendent order * 2. Render prism in ancestor -\> descendent order
* 3. Not set off React's concurrent mode alarms * 3. Not set off React's concurrent mode alarms
* *
* *
@ -266,21 +266,21 @@ function queueIfNeeded() {
* *
* Notes on the latest implementation: * Notes on the latest implementation:
* *
* # Remove cold derivation reads * # Remove cold prism reads
* *
* Prior to the latest change, the first render of every `useDerivation()` resulted in a cold read of its inner derivation. * Prior to the latest change, the first render of every `useDerivation()` resulted in a cold read of its inner prism.
* Cold reads are predictably slow. The reason we'd run cold reads was to comply with react's rule of not running side-effects * Cold reads are predictably slow. The reason we'd run cold reads was to comply with react's rule of not running side-effects
* during render. (Turning a derivation hot is _technically_ a side-effect). * during render. (Turning a prism hot is _technically_ a side-effect).
* *
* However, now that users are animating scenes with hundreds of objects in the same sequence, the lag started to be noticable. * However, now that users are animating scenes with hundreds of objects in the same sequence, the lag started to be noticable.
* *
* This commit changes `useDerivation()` so that it turns its derivation hot before rendering them. * This commit changes `useDerivation()` so that it turns its prism hot before rendering them.
* *
* # Freshen derivations before render * # Freshen prisms before render
* *
* Previously in order to avoid the zombie child problem (https://kaihao.dev/posts/stale-props-and-zombie-children-in-redux) * Previously in order to avoid the zombie child problem (https://kaihao.dev/posts/stale-props-and-zombie-children-in-redux)
* we deferred freshening the derivations to the render phase of components. This meant that if a derivation's dependencies * we deferred freshening the prisms to the render phase of components. This meant that if a prism's dependencies
* changed, `useDerivation()` would schedule a re-render, regardless of whether that change actually affected the derivation's * changed, `useDerivation()` would schedule a re-render, regardless of whether that change actually affected the prism's
* value. Here is a contrived example: * value. Here is a contrived example:
* *
* ```ts * ```ts
@ -294,10 +294,10 @@ function queueIfNeeded() {
* num.set(2) // would cause Comp to re-render- even though 1 is still a positive number * num.set(2) // would cause Comp to re-render- even though 1 is still a positive number
* ``` * ```
* *
* We now avoid this problem by freshening the derivation (i.e. calling `der.getValue()`) inside `runQueue()`, * We now avoid this problem by freshening the prism (i.e. calling `der.getValue()`) inside `runQueue()`,
* and then only causing a re-render if the derivation's value is actually changed. * and then only causing a re-render if the prism's value is actually changed.
* *
* This still avoids the zombie-child problem because `runQueue` reads the derivations in-order of their position in * This still avoids the zombie-child problem because `runQueue` reads the prisms in-order of their position in
* the mounting tree. * the mounting tree.
* *
* On the off-chance that one of them still turns out to be a zombile child, `runQueue` will defer that particular * On the off-chance that one of them still turns out to be a zombile child, `runQueue` will defer that particular
@ -374,29 +374,29 @@ export function useDerivation<T>(der: Prism<T>, debugLabel?: string): T {
} }
/** /**
* This makes sure the prism derivation remains hot as long as the * This makes sure the prism prism remains hot as long as the
* component calling the hook is alive, but it does not * component calling the hook is alive, but it does not
* return the value of the derivation, and it does not * return the value of the prism, and it does not
* re-render the component if the value of the derivation changes. * re-render the component if the value of the prism changes.
* *
* Use this hook if you plan to read a derivation in a * Use this hook if you plan to read a prism in a
* useEffect() call, without the derivation causing your * useEffect() call, without the prism causing your
* element to re-render. * element to re-render.
*/ */
export function usePrismWithoutReRender<T>( export function usePrismWithoutReRender<T>(
fn: () => T, fn: () => T,
deps: unknown[], deps: unknown[],
): Prism<T> { ): Prism<T> {
const derivation = useMemo(() => prism(fn), deps) const pr = useMemo(() => prism(fn), deps)
return useDerivationWithoutReRender(derivation) return useDerivationWithoutReRender(pr)
} }
/** /**
* This makes sure the derivation remains hot as long as the * This makes sure the prism remains hot as long as the
* component calling the hook is alive, but it does not * component calling the hook is alive, but it does not
* return the value of the derivation, and it does not * return the value of the prism, and it does not
* re-render the component if the value of the derivation changes. * re-render the component if the value of the prism changes.
*/ */
export function useDerivationWithoutReRender<T>(der: Prism<T>): Prism<T> { export function useDerivationWithoutReRender<T>(der: Prism<T>): Prism<T> {
useEffect(() => { useEffect(() => {

View file

@ -155,17 +155,13 @@ export function onChange<P extends PointerType<$IntentionalAny>>(
callback: (value: P extends PointerType<infer T> ? T : unknown) => void, callback: (value: P extends PointerType<infer T> ? T : unknown) => void,
): VoidFn { ): VoidFn {
if (isPointer(pointer)) { if (isPointer(pointer)) {
const derivation = pointerToPrism(pointer) const pr = pointerToPrism(pointer)
return derivation.onChange( return pr.onChange(getCoreTicker(), callback as $IntentionalAny, true)
getCoreTicker(),
callback as $IntentionalAny,
true,
)
} else if (isPrism(pointer)) { } else if (isPrism(pointer)) {
return pointer.onChange(getCoreTicker(), callback as $IntentionalAny, true) return pointer.onChange(getCoreTicker(), callback as $IntentionalAny, true)
} else { } else {
throw new Error( throw new Error(
`Called onChange(p) where p is neither a pointer nor a derivation.`, `Called onChange(p) where p is neither a pointer nor a prism.`,
) )
} }
} }

View file

@ -7,7 +7,7 @@ import globals from '@theatre/shared/globals'
/** /**
* @remarks * @remarks
* TODO this could be turned into a simple derivation, like: * TODO this could be turned into a simple prism, like:
* `editor.isReady: Prism<{isReady: true} | {isReady: false, reason: 'conflictBetweenDiskStateAndBrowserState'}>` * `editor.isReady: Prism<{isReady: true} | {isReady: false, reason: 'conflictBetweenDiskStateAndBrowserState'}>`
*/ */
export default async function initialiseProjectState( export default async function initialiseProjectState(

View file

@ -67,7 +67,7 @@ export default class Sequence {
) )
this._statePointerDerivation = prism( this._statePointerDerivation = prism(
() => this._playbackControllerBox.derivation.getValue().statePointer, () => this._playbackControllerBox.prism.getValue().statePointer,
) )
this._positionD = prism(() => { this._positionD = prism(() => {
@ -189,7 +189,7 @@ export default class Sequence {
* @remarks * @remarks
* One use case for this is to play the playback within the focus range. * One use case for this is to play the playback within the focus range.
* *
* @param rangeD - The derivation that contains the range that will be used for the playback * @param rangeD - The prism that contains the range that will be used for the playback
* *
* @returns a promise that gets rejected if the playback stopped for whatever reason * @returns a promise that gets rejected if the playback stopped for whatever reason
* *

View file

@ -31,7 +31,7 @@ export interface IPlaybackController {
* @remarks * @remarks
* One use case for this is to play the playback within the focus range. * One use case for this is to play the playback within the focus range.
* *
* @param rangeD - The derivation that contains the range that will be used for the playback * @param rangeD - The prism that contains the range that will be used for the playback
* *
* @returns a promise that gets rejected if the playback stopped for whatever reason * @returns a promise that gets rejected if the playback stopped for whatever reason
* *

View file

@ -70,8 +70,8 @@ export default class SheetObject implements IdentityPrismProvider {
} }
getValues(): Prism<Pointer<SheetObjectPropsValue>> { getValues(): Prism<Pointer<SheetObjectPropsValue>> {
// Cache the derivation because only one is needed per SheetObject. // Cache the prism because only one is needed per SheetObject.
// Also, if `onValuesChange()` is unsubscribed from, this derivation will go cold // Also, if `onValuesChange()` is unsubscribed from, this prism will go cold
// and free its resources. So it's no problem to still keep it on the cache. // and free its resources. So it's no problem to still keep it on the cache.
return this._cache.get('getValues()', () => return this._cache.get('getValues()', () =>
prism(() => { prism(() => {
@ -107,7 +107,7 @@ export default class SheetObject implements IdentityPrismProvider {
/** /**
* The lowest layer is the default value of the root prop. Since an object's config * The lowest layer is the default value of the root prop. Since an object's config
* _could_ change, we read it as a derivation. Otherwise, we could have just `getDefaultsOfPropTypeConfig(this.template.staticConfig)`. * _could_ change, we read it as a prism. Otherwise, we could have just `getDefaultsOfPropTypeConfig(this.template.staticConfig)`.
* *
* Note: If studio is not present, there is no known use-case for the config of an object to change on the fly, so * Note: If studio is not present, there is no known use-case for the config of an object to change on the fly, so
* we could read this value statically. * we could read this value statically.
@ -168,7 +168,7 @@ export default class SheetObject implements IdentityPrismProvider {
let sequenced let sequenced
{ {
// NOTE: we're reading the sequenced values as a derivation to a pointer. This should be refactored // NOTE: we're reading the sequenced values as a prism to a pointer. This should be refactored
// to a simple pointer. // to a simple pointer.
const pointerToSequencedValuesD = prism.memo( const pointerToSequencedValuesD = prism.memo(
'seq', 'seq',
@ -185,7 +185,7 @@ export default class SheetObject implements IdentityPrismProvider {
) )
// read the sequenced values // read the sequenced values
// (val(val(x))) unwraps the pointer and the derivation // (val(val(x))) unwraps the pointer and the prism
sequenced = val(val(pointerToSequencedValuesD)) sequenced = val(val(pointerToSequencedValuesD))
// deep-merge the sequenced values with the previous layer // deep-merge the sequenced values with the previous layer
@ -243,7 +243,7 @@ export default class SheetObject implements IdentityPrismProvider {
const untaps: Array<() => void> = [] const untaps: Array<() => void> = []
for (const {trackId, pathToProp} of tracksToProcess) { for (const {trackId, pathToProp} of tracksToProcess) {
const derivation = this._trackIdToDerivation(trackId) const pr = this._trackIdToPrism(trackId)
const propConfig = getPropConfigByPath( const propConfig = getPropConfigByPath(
config, config,
pathToProp, pathToProp,
@ -254,7 +254,7 @@ export default class SheetObject implements IdentityPrismProvider {
propConfig.interpolate! as Interpolator<$IntentionalAny> propConfig.interpolate! as Interpolator<$IntentionalAny>
const updateSequenceValueFromItsDerivation = () => { const updateSequenceValueFromItsDerivation = () => {
const triple = derivation.getValue() const triple = pr.getValue()
if (!triple) return valsAtom.setIn(pathToProp, undefined) if (!triple) return valsAtom.setIn(pathToProp, undefined)
@ -279,9 +279,7 @@ export default class SheetObject implements IdentityPrismProvider {
interpolate(left, right, triple.progression), interpolate(left, right, triple.progression),
) )
} }
const untap = derivation.onStale( const untap = pr.onStale(updateSequenceValueFromItsDerivation)
updateSequenceValueFromItsDerivation,
)
updateSequenceValueFromItsDerivation() updateSequenceValueFromItsDerivation()
untaps.push(untap) untaps.push(untap)
@ -299,7 +297,7 @@ export default class SheetObject implements IdentityPrismProvider {
}) })
} }
protected _trackIdToDerivation( protected _trackIdToPrism(
trackId: SequenceTrackId, trackId: SequenceTrackId,
): Prism<InterpolationTriple | undefined> { ): Prism<InterpolationTriple | undefined> {
const trackP = const trackP =

View file

@ -147,7 +147,7 @@ export default class TheatreSheetObject<
return {...privateAPI(this).address} return {...privateAPI(this).address}
} }
private _valuesDerivation(): Prism<this['value']> { private _valuesPrism(): Prism<this['value']> {
return this._cache.get('onValuesChangeDerivation', () => { return this._cache.get('onValuesChangeDerivation', () => {
const sheetObject = privateAPI(this) const sheetObject = privateAPI(this)
const d: Prism<PropsValue<Props>> = prism(() => { const d: Prism<PropsValue<Props>> = prism(() => {
@ -158,15 +158,15 @@ export default class TheatreSheetObject<
} }
onValuesChange(fn: (values: this['value']) => void): VoidFn { onValuesChange(fn: (values: this['value']) => void): VoidFn {
return this._valuesDerivation().onChange(getCoreTicker(), fn, true) return this._valuesPrism().onChange(getCoreTicker(), fn, true)
} }
// internal: Make the deviration keepHot if directly read // internal: Make the deviration keepHot if directly read
get value(): PropsValue<Props> { get value(): PropsValue<Props> {
const der = this._valuesDerivation() const der = this._valuesPrism()
if (KEEP_HOT_FOR_MS != null) { if (KEEP_HOT_FOR_MS != null) {
if (!der.isHot) { if (!der.isHot) {
// derivation not hot, so keep it hot and set up `_keepHotUntapDebounce` // prism not hot, so keep it hot and set up `_keepHotUntapDebounce`
if (this._keepHotUntapDebounce != null) { if (this._keepHotUntapDebounce != null) {
// defensive checks // defensive checks
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {

View file

@ -54,7 +54,7 @@ export default function useKeyboardShortcuts() {
const {projectId, sheetId} = seq.address const {projectId, sheetId} = seq.address
/* /*
* The value of this derivation is an array that contains the * The value of this prism is an array that contains the
* range of the playback (start and end), and a boolean that is * range of the playback (start and end), and a boolean that is
* `true` if the playback should be played within that range. * `true` if the playback should be played within that range.
*/ */
@ -162,7 +162,7 @@ const getPlaybackStateBox = memoizeFn(
) )
/* /*
* A memoized function that returns a derivation with a boolean value. * A memoized function that returns a prism with a boolean value.
* This value is set to `true` if: * This value is set to `true` if:
* 1. the playback is playing and using the focus range instead of the whole sequence * 1. the playback is playing and using the focus range instead of the whole sequence
* 2. the playback is stopped, but would use the focus range if it were started. * 2. the playback is stopped, but would use the focus range if it were started.
@ -171,7 +171,7 @@ export const getIsPlayheadAttachedToFocusRange = memoizeFn(
(sequence: Sequence) => (sequence: Sequence) =>
prism<boolean>(() => { prism<boolean>(() => {
const controlledPlaybackState = const controlledPlaybackState =
getPlaybackStateBox(sequence).derivation.getValue() getPlaybackStateBox(sequence).prism.getValue()
if (controlledPlaybackState) { if (controlledPlaybackState) {
return controlledPlaybackState.getValue().isFollowingARange return controlledPlaybackState.getValue().isFollowingARange
} else { } else {

View file

@ -199,8 +199,8 @@ const isDetailPanelHotspotActiveB = new Box<boolean>(false)
const isDetailPanelHoveredB = new Box<boolean>(false) const isDetailPanelHoveredB = new Box<boolean>(false)
export const shouldShowDetailD = prism<boolean>(() => { export const shouldShowDetailD = prism<boolean>(() => {
const isHovered = val(isDetailPanelHoveredB.derivation) const isHovered = val(isDetailPanelHoveredB.prism)
const isHotspotActive = val(isDetailPanelHotspotActiveB.derivation) const isHotspotActive = val(isDetailPanelHotspotActiveB.prism)
return isHovered || isHotspotActive return isHovered || isHotspotActive
}) })

View file

@ -83,8 +83,8 @@ const isOutlinePanelHotspotActiveB = new Box<boolean>(false)
const isOutlinePanelHoveredB = new Box<boolean>(false) const isOutlinePanelHoveredB = new Box<boolean>(false)
export const shouldShowOutlineD = prism<boolean>(() => { export const shouldShowOutlineD = prism<boolean>(() => {
const isHovered = val(isOutlinePanelHoveredB.derivation) const isHovered = val(isOutlinePanelHoveredB.prism)
const isHotspotActive = val(isOutlinePanelHotspotActiveB.derivation) const isHotspotActive = val(isOutlinePanelHotspotActiveB.prism)
return isHovered || isHotspotActive return isHovered || isHotspotActive
}) })

View file

@ -545,12 +545,12 @@ const {isCurveEditorOpenD, isConnectionEditingInCurvePopover, getLock} =
} }
}, },
isCurveEditorOpenD: prism(() => { isCurveEditorOpenD: prism(() => {
return connectionsInCurvePopoverEdit.derivation.getValue().length > 0 return connectionsInCurvePopoverEdit.prism.getValue().length > 0
}), }),
// must be run in a prism // must be run in a prism
isConnectionEditingInCurvePopover(con: KeyframeConnectionWithAddress) { isConnectionEditingInCurvePopover(con: KeyframeConnectionWithAddress) {
prism.ensurePrism() prism.ensurePrism()
return connectionsInCurvePopoverEdit.derivation return connectionsInCurvePopoverEdit.prism
.getValue() .getValue()
.some( .some(
({left, right}) => ({left, right}) =>

View file

@ -78,7 +78,7 @@ const stateB = new Box<
} }
>({mode: 'snapToNone'}) >({mode: 'snapToNone'})
export const snapPositionsStateD = stateB.derivation export const snapPositionsStateD = stateB.prism
export function snapToAll() { export function snapToAll() {
stateB.set({mode: 'snapToAll'}) stateB.set({mode: 'snapToAll'})

View file

@ -40,7 +40,7 @@ const ExtensionToolsetRender: React.FC<{
if (typeof detach === 'function') return detach if (typeof detach === 'function') return detach
}, [extension, toolbarId]) }, [extension, toolbarId])
const config = useVal(toolsetConfigBox.derivation) const config = useVal(toolsetConfigBox.prism)
return <Toolset config={config} /> return <Toolset config={config} />
} }

View file

@ -41,7 +41,7 @@ export const useTooltipOpenState = (): [
const TooltipContext: React.FC<{}> = ({children}) => { const TooltipContext: React.FC<{}> = ({children}) => {
const currentTooltipId = useMemo(() => new Box(-1), []) const currentTooltipId = useMemo(() => new Box(-1), [])
const cur = currentTooltipId.derivation const cur = currentTooltipId.prism
const set = useMemo(() => { const set = useMemo(() => {
let lastTimeout: NodeJS.Timeout | undefined = undefined let lastTimeout: NodeJS.Timeout | undefined = undefined

View file

@ -68,7 +68,7 @@ function createPresenceContext(options: {
const focusD = useMemo(() => { const focusD = useMemo(() => {
if (!itemKey) return undefinedD if (!itemKey) return undefinedD
// this is the thing being hovered // this is the thing being hovered
const currentD = currentUserHoverItemB.derivation const currentD = currentUserHoverItemB.prism
const primaryFocusDer = pointerToPrism( const primaryFocusDer = pointerToPrism(
currentUserHoverFlagItemsAtom.pointer[itemKey], currentUserHoverFlagItemsAtom.pointer[itemKey],
) )

View file

@ -1,7 +1,7 @@
import {prism} from '@theatre/dataverse' import {prism} from '@theatre/dataverse'
/** /**
* A derivation that holds the current mouse position. * A prism that holds the current mouse position.
*/ */
const mousePositionD = prism(() => { const mousePositionD = prism(() => {
const [pos, setPos] = prism.state<MouseEvent | null>('pos', null) const [pos, setPos] = prism.state<MouseEvent | null>('pos', null)