This commit is contained in:
Aaron Smith 2023-09-07 18:11:25 +01:00
parent 470d2b5407
commit 574eb8e4c5
4 changed files with 29 additions and 21 deletions

View file

@ -9,17 +9,20 @@ export type Scope = {
[number]: Scope -- children [number]: Scope -- children
} }
export type Node<T> = { export type StartNode<T> = {
scope: Scope,
cache: T, cache: T,
[number]: Node<T>
}
export type Node<T> = StartNode<T> & {
scope: Scope,
effect: (T) -> (), effect: (T) -> (),
[number]: Node<T> -- children
} }
-- flag used to detect when node reference capturing is active -- flag used to detect when node reference capturing is active
local reff = false local reff = false
-- array of all nodes referenced since above flag was set -- array of all nodes referenced since above flag was set
local refs = {} :: { Node<unknown> } local refs = {} :: { StartNode<unknown> }
local scopes = { n = 0 } :: { [number]: Scope, n: number } local scopes = { n = 0 } :: { [number]: Scope, n: number }
@ -102,7 +105,7 @@ local function run_effect<T>(node: Node<T>)
node.effect(node.cache) node.effect(node.cache)
end end
local function add_child(parent: Node<any>, child: Node<any>) local function add_child(parent: StartNode<any>, child: Node<any>)
table.insert(parent, child) table.insert(parent, child)
end end
@ -120,7 +123,7 @@ local function destroy(scope: Scope)
end end
-- runs node effects, recalculates descendants and runs descendant effects -- runs node effects, recalculates descendants and runs descendant effects
local function update<T>(node: Node<T>) local function update<T>(node: StartNode<T>)
for _, child in ipairs(node) do for _, child in ipairs(node) do
local scope = child.scope local scope = child.scope
assert(scope) assert(scope)
@ -133,7 +136,7 @@ local function update<T>(node: Node<T>)
end end
-- detect what nodes were referenced in the given callback and returns them in an array -- detect what nodes were referenced in the given callback and returns them in an array
local function capture<T, U>(fn: (U?) -> T, arg: U?): ({ Node<unknown> }, T) local function capture<T, U>(fn: (U?) -> T, arg: U?): ({ StartNode<unknown> }, T)
if reff then throw("recursive capture detected") end if reff then throw("recursive capture detected") end
table.clear(refs) table.clear(refs)
@ -164,7 +167,7 @@ local function capture_parents<T, U>(child: Node<T>, fn: (U?) -> T, arg: U?): T
return result return result
end end
local function track<T>(node: Node<T>) local function track<T>(node: StartNode<T>)
if reff then table.insert(refs, node :: Node<any>) end if reff then table.insert(refs, node :: Node<any>) end
end end
@ -176,13 +179,15 @@ local function create_scope(): Scope
end end
local function create_node<T>(value: T): Node<T> local function create_node<T>(value: T): Node<T>
local node = { return {
scope = create_scope(), scope = create_scope(),
cache = value, cache = value,
effect = function() end, effect = function() end,
} }
end
return node local function create_start_node<T>(value: T): StartNode<T>
return { cache = value }
end end
return table.freeze { return table.freeze {
@ -199,6 +204,7 @@ return table.freeze {
capture = capture, capture = capture,
capture_parents = capture_parents, capture_parents = capture_parents,
create_node = create_node, create_node = create_node,
create_start_node = create_start_node,
create_scope = create_scope, create_scope = create_scope,
refs = refs refs = refs
} }

View file

@ -6,8 +6,9 @@ local throw = require(script.Parent.throw)
local flags = require(script.Parent.flags) local flags = require(script.Parent.flags)
local graph = require(script.Parent.graph) local graph = require(script.Parent.graph)
type Scope = graph.Scope type Scope = graph.Scope
type Node<T> = graph.Node<T> type StartNode<T> = graph.StartNode<T>
local create_node = graph.create_node local create_node = graph.create_node
local create_start_node = graph.create_start_node
local create_scope = graph.create_scope local create_scope = graph.create_scope
local track = graph.track local track = graph.track
local update = graph.update local update = graph.update
@ -38,7 +39,7 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
local input_cache = {} :: Map<K, VI> local input_cache = {} :: Map<K, VI>
local output_cache = {} :: Map<K, VO> local output_cache = {} :: Map<K, VO>
local input_nodes = {} :: Map<K, Node<VI>> local input_nodes = {} :: Map<K, StartNode<VI>>
local remove_queue = {} :: { K } local remove_queue = {} :: { K }
local output_array = {} :: { VO } local output_array = {} :: { VO }
@ -77,7 +78,7 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
open_scope(scope) open_scope(scope)
local node = create_node(v) local node = create_start_node(v)
input_nodes[i] = node input_nodes[i] = node
input_cache[i] = v input_cache[i] = v
output_cache[i] = transform(function() output_cache[i] = transform(function()
@ -131,7 +132,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
local new_input_cache_up = {} :: Map<VI, K> local new_input_cache_up = {} :: Map<VI, K>
local output_cache = {} :: Map<VI, VO> local output_cache = {} :: Map<VI, VO>
local input_nodes = {} :: Map<VI, Node<K>> local input_nodes = {} :: Map<VI, StartNode<K>>
local output_array = {} :: { VO } local output_array = {} :: { VO }
local scopes = {} :: Map<VI, Scope> local scopes = {} :: Map<VI, Scope>
@ -163,7 +164,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
open_scope(scope) open_scope(scope)
local node = create_node(i) local node = create_start_node(i)
input_nodes[v] = node input_nodes[v] = node
output_cache[v] = transform(v, function() output_cache[v] = transform(v, function()
track(node) track(node)

View file

@ -2,7 +2,7 @@ if not game then script = require "test/relative-string" end
local graph = require(script.Parent.graph) local graph = require(script.Parent.graph)
type Node<T> = graph.Node<T> type Node<T> = graph.Node<T>
local create_node = graph.create_node local create_start_node = graph.create_start_node
local get_scope = graph.get_scope local get_scope = graph.get_scope
local track = graph.track local track = graph.track
local update = graph.update local update = graph.update
@ -10,9 +10,9 @@ local update = graph.update
export type Source<T> = (() -> T) & ((T) -> T) export type Source<T> = (() -> T) & ((T) -> T)
local function source<T>(initial_value: T): Source<T> local function source<T>(initial_value: T): Source<T>
assert(get_scope()) --assert(get_scope())
local node = create_node(initial_value) local node = create_start_node(initial_value)
return function(...): T return function(...): T
if select("#", ...) == 0 then -- no args were given if select("#", ...) == 0 then -- no args were given

View file

@ -24,7 +24,9 @@ Unsupported datatypes:
local throw = require(script.Parent.throw) local throw = require(script.Parent.throw)
local graph = require(script.Parent.graph) local graph = require(script.Parent.graph)
type Node<T> = graph.Node<T> type Node<T> = graph.Node<T>
type StartNode<T> = graph.StartNode<T>
local create_node = graph.create_node local create_node = graph.create_node
local create_start_node = graph.create_start_node
local update = graph.update local update = graph.update
local capture = graph.capture local capture = graph.capture
local add_child = graph.add_child local add_child = graph.add_child
@ -142,7 +144,7 @@ setmetatable(vec6_to_type, invalid_type)
-- maps spring data to its corresponding output node -- maps spring data to its corresponding output node
-- lifetime of spring data is tied to output node -- lifetime of spring data is tied to output node
local springs: { [SpringData<any>]: Node<any> } = {} local springs: { [SpringData<any>]: StartNode<any> } = {}
setmetatable(springs, { __mode = "v" }) setmetatable(springs, { __mode = "v" })
local function spring<T>(source: () -> T, period: number?, damping_ratio: number?): () -> T local function spring<T>(source: () -> T, period: number?, damping_ratio: number?): () -> T
@ -177,8 +179,7 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
source_value = initial_value, source_value = initial_value,
} }
local output = create_node(initial_value) local output = create_start_node(initial_value)
local updater = create_node(false) local updater = create_node(false)
updater.effect = function() updater.effect = function()