From 0046af55981fc8a52f731b8b8a4b62ecd3ab0c9b Mon Sep 17 00:00:00 2001 From: Richard Date: Fri, 29 Mar 2024 11:47:46 -0700 Subject: [PATCH] refactor: rename `Source` to `Molecule` --- src/atom.luau | 10 +++++++++- src/capture.luau | 10 +++++----- src/derive.luau | 8 ++++---- src/effect.luau | 6 +++--- src/index.d.ts | 43 +++++++++++++++++++++++-------------------- src/init.luau | 2 +- src/map.luau | 14 +++++++------- src/observe.luau | 6 +++--- src/store.luau | 1 - src/subscribe.luau | 8 ++++---- src/types.luau | 2 +- 11 files changed, 60 insertions(+), 50 deletions(-) diff --git a/src/atom.luau b/src/atom.luau index 3dd550d..b39cd97 100644 --- a/src/atom.luau +++ b/src/atom.luau @@ -1,5 +1,6 @@ local store = require(script.Parent.store) local types = require(script.Parent.types) +local unwrap = require(script.Parent.unwrap) type Atom = types.Atom type AtomOptions = types.AtomOptions @@ -15,7 +16,14 @@ local function atom(state: T, options: AtomOptions?): Atom return state end - local nextState = if type(...) == "function" then (...)(state) else ... + local nextState = ... + + if type(nextState) == "function" then + -- Atoms called in a function should not be included as dependencies + nextState = unwrap(function() + return nextState(state) + end) + end if state ~= nextState and not (equals and equals(state, nextState)) then state = nextState diff --git a/src/capture.luau b/src/capture.luau index 111e52c..9526de9 100644 --- a/src/capture.luau +++ b/src/capture.luau @@ -1,15 +1,15 @@ local store = require(script.Parent.store) local types = require(script.Parent.types) type Atom = types.Atom -type Source = types.Source +type Molecule = types.Molecule -local function capture(source: Source): ({ [Atom]: true }, T) - if store.listeners[source] then - return { [source] = true }, source() +local function capture(molecule: Molecule): ({ [Atom]: true }, T) + if store.listeners[molecule] then + return { [molecule] = true }, molecule() end store.setCapturing(true) - local success, result = pcall(source) + local success, result = pcall(molecule) store.setCapturing(false) local dependencies = table.clone(store.captured) diff --git a/src/derive.luau b/src/derive.luau index 444db17..5613013 100644 --- a/src/derive.luau +++ b/src/derive.luau @@ -4,16 +4,16 @@ local store = require(script.Parent.store) local types = require(script.Parent.types) type Atom = types.Atom type AtomOptions = types.AtomOptions -type Source = types.Source +type Molecule = types.Molecule -local function derive(source: Source, options: AtomOptions?): Source - local atoms, state = capture(source) +local function derive(molecule: Molecule, options: AtomOptions?): Molecule + local atoms, state = capture(molecule) local derived = atom(state, options) local derivedRef = setmetatable({ current = derived }, { __mode = "v" }) local function listener() if derivedRef.current then - derivedRef.current(source()) + derivedRef.current(molecule()) end end diff --git a/src/effect.luau b/src/effect.luau index 8a6e685..78589d9 100644 --- a/src/effect.luau +++ b/src/effect.luau @@ -1,11 +1,11 @@ local capture = require(script.Parent.capture) local store = require(script.Parent.store) -local function effect(source: () -> nil): () -> () - local atoms = capture(source) +local function effect(molecule: () -> nil): () -> () + local atoms = capture(molecule) local function listener() - source() + molecule() end for atom in atoms do diff --git a/src/index.d.ts b/src/index.d.ts index eb3e721..03ae5c0 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -9,12 +9,12 @@ type AnyMap = | (K extends string | number | symbol ? { readonly [Key in K]: V } : never); declare namespace Charm { - interface Atom extends Source { + interface Atom extends Molecule { readonly __nominal: unique symbol; - (state: T | ((prev: T) => T)): void; + (state: State | ((prev: State) => State)): void; } - type Source = () => T; + type Molecule = () => State; interface AtomOptions { equals?: (prev: State, next: State) => boolean; @@ -24,52 +24,55 @@ declare namespace Charm { function isAtom(value: unknown): value is Atom; - function derive(atom: Source, options?: AtomOptions): Source; + function derive(molecule: Molecule, options?: AtomOptions): Molecule; - function subscribe(atom: Source, callback: (state: State, prev: State) => void): Cleanup; + function subscribe(molecule: Molecule, callback: (state: State, prev: State) => void): Cleanup; - function effect(callback: Source): Cleanup; + function effect(callback: Molecule): Cleanup; - function unwrap(atom: State | Source): State; + function unwrap(molecule: State | Molecule): State; - function capture(atom: Source): LuaTuple<[captured: Set>, state: State]>; + function capture(molecule: Molecule): LuaTuple<[captured: Set>, state: State]>; function observe( - atom: Source, + molecule: Molecule, factory: (item: Item, index: number) => Cleanup | void, ): Cleanup; function observe( - atom: Source>, + molecule: Molecule>, factory: (item: Item, key: Key) => Cleanup | void, ): Cleanup; function map( - atom: Source, + molecule: Molecule, mapper: (value: V0, index: number) => LuaTuple<[value: V1 | undefined, key: K1]>, - ): Source>; + ): Molecule>; - function map(atom: Source, mapper: (value: V0, index: number) => V1): Source; + function map( + molecule: Molecule, + mapper: (value: V0, index: number) => V1, + ): Molecule; function map( - atom: Source>, + molecule: Molecule>, mapper: (value: V0, key: K0) => LuaTuple<[value: V1 | undefined, key: K1]> | V1, - ): Source>; + ): Molecule>; // React - function useAtomState(atom: Source): State; + function useAtomState(molecule: Molecule): State; - function useAtomState(atom: Source, selector: (state: State) => Result): Result; + function useAtomState(molecule: Molecule, selector: (state: State) => Result): Result; - function useSetAtom(atom: Source): (state: State | ((prev: State) => State)) => void; + function useSetAtom(molecule: Molecule): (state: State | ((prev: State) => State)) => void; function useAtom( - atom: Source, + molecule: Molecule, ): LuaTuple<[state: State, setState: (state: State | ((prev: State) => State)) => void]>; function useAtom( - atom: Source, + molecule: Molecule, selector: (state: State) => Result, ): LuaTuple<[state: Result, setState: (state: Result | ((prev: Result) => Result)) => void]>; diff --git a/src/init.luau b/src/init.luau index 00ba0b3..0464998 100644 --- a/src/init.luau +++ b/src/init.luau @@ -16,7 +16,7 @@ local types = require(script.types) local unwrap = require(script.unwrap) export type Atom = types.Atom -export type Source = types.Source +export type Molecule = types.Molecule export type SyncPayload = types.SyncPayload return { diff --git a/src/map.luau b/src/map.luau index 3024d64..29bff08 100644 --- a/src/map.luau +++ b/src/map.luau @@ -2,14 +2,14 @@ local atom = require(script.Parent.atom) local subscribe = require(script.Parent.subscribe) local types = require(script.Parent.types) type Atom = types.Atom -type Source = types.Source +type Molecule = types.Molecule type Map = - ((atom: Source<{ [K0]: V0 }>, mapper: (V0, K0) -> (V1?, K1)) -> Source<{ [K1]: V1 }>) - & ((atom: Source<{ [K0]: V0 }>, mapper: (V0, K0) -> V1?) -> Source<{ [K0]: V1 }>) - & ((atom: Source<{ [K0]: V0 }>, mapper: (V0, K0) -> (V1?, K1?)) -> Source<{ [K1]: V1 }>) + ((fn: Molecule<{ [K0]: V0 }>, mapper: (V0, K0) -> (V1?, K1)) -> Molecule<{ [K1]: V1 }>) + & ((fn: Molecule<{ [K0]: V0 }>, mapper: (V0, K0) -> V1?) -> Molecule<{ [K0]: V1 }>) + & ((fn: Molecule<{ [K0]: V0 }>, mapper: (V0, K0) -> (V1?, K1?)) -> Molecule<{ [K1]: V1 }>) -local function map(itemsAtom: Source<{ [K0]: V0 }>, mapper: (V0, K0) -> (V1?, K1?)): Source<{ [K1]: V1 }> +local function map(getItems: Molecule<{ [K0]: V0 }>, mapper: (V0, K0) -> (V1?, K1?)): Molecule<{ [K1]: V1 }> local mappedAtom = atom({}) local mappedAtomRef = setmetatable({ current = mappedAtom }, { __mode = "v" }) local prevMappedItems: { [K1]: V1 } = {} @@ -45,8 +45,8 @@ local function map(itemsAtom: Source<{ [K0]: V0 }>, mapper: (V0, mappedAtom(mappedItems) end - unsubscribe = subscribe(itemsAtom, listener) - listener(itemsAtom()) + unsubscribe = subscribe(getItems, listener) + listener(getItems()) return mappedAtom end diff --git a/src/observe.luau b/src/observe.luau index 7954a39..52f8d19 100644 --- a/src/observe.luau +++ b/src/observe.luau @@ -1,12 +1,12 @@ local effect = require(script.Parent.effect) local types = require(script.Parent.types) -type Source = types.Source +type Molecule = types.Molecule -local function observe(source: Source<{ [K]: V }>, factory: (value: V, key: K) -> () -> ()): () -> () +local function observe(molecule: Molecule<{ [K]: V }>, factory: (value: V, key: K) -> () -> ()): () -> () local connections: { [K]: () -> () } = {} local unsubscribe = effect(function() - local state = source() + local state = molecule() for key, disconnect in connections do if state[key] == nil then diff --git a/src/store.luau b/src/store.luau index 215c3d6..d8b354b 100644 --- a/src/store.luau +++ b/src/store.luau @@ -1,6 +1,5 @@ local types = require(script.Parent.types) type Atom = types.Atom -type Source = types.Source type WeakMap = types.WeakMap local listeners: WeakMap, WeakMap<() -> (), unknown>> = setmetatable({}, { __mode = "k" }) diff --git a/src/subscribe.luau b/src/subscribe.luau index e1d3786..b471db3 100644 --- a/src/subscribe.luau +++ b/src/subscribe.luau @@ -1,14 +1,14 @@ local capture = require(script.Parent.capture) local store = require(script.Parent.store) local types = require(script.Parent.types) -type Source = types.Source +type Molecule = types.Molecule -local function subscribe(source: Source, callback: (state: T, prev: T) -> ()): () -> () - local atoms, state = capture(source) +local function subscribe(molecule: Molecule, callback: (state: T, prev: T) -> ()): () -> () + local atoms, state = capture(molecule) local function listener() local prevState = state - state = source() + state = molecule() callback(state, prevState) end diff --git a/src/types.luau b/src/types.luau index f475b0f..8c7dac4 100644 --- a/src/types.luau +++ b/src/types.luau @@ -2,7 +2,7 @@ export type WeakMap = typeof(setmetatable({} :: { [K]: V }, { __mode = "k" export type Atom = (() -> T) & (state: T | (T) -> T) -> T -export type Source = () -> T +export type Molecule = () -> T export type AtomOptions = { equals: (prev: T, next: T) -> boolean,