Skip to content

feature: dynamic signals 1 #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 17, 2024
Merged

feature: dynamic signals 1 #9

merged 5 commits into from
Jun 17, 2024

Conversation

omar-azmi
Copy link
Owner

description:

This PR introduces two dynamic signals that hold a collection of Accessors:

  • UnisetSignal: holds a set of accessors (Set<Accessor<T>>), and when its value is requested, it returns the values of the accessors it is holding (as a Set<T>).
  • ListSignal: holds an array of accessors (RcList<Accessor<T>>), and when its value is requested, it returns an array of the values of the accessors it is holding (as a List<T>).
    • The List data structure is just your plain old Array with the addition of a few useful methods (insert, delete, swap) that make it similar to python-style lists.
      In addition, it requires that you assign elemets using the list.set(index, value) method, instead of the index based assignment list[index] = value. This is so that its subclasses can keep track of what assignments are taking place (without having to resort to nasty proxies).
    • The RcList is a subclass of the List class, but it reference counts the elements that are added or removed from it.
      This feature is necessary for its signal compatible subclass ListCollection to be able to dynamically tell which Accessors the signal is dependant on, and which of them are being dynamically removed completely (with no duplicates remaining) to de-allocate its dependency on them.

This PR builds on the Identifiable signal feature introduced in #8 , which simplifies the ability to tell the signal id of a signal jsut from its Accessor.

justification:

Previously, basic dynamic collection based tasks were incredibly difficult to implement, and needed to be tailored to the specific datatype structure that was being held in a collection.
This is because the library formerly focused additive signals, where, by design, new signals that were created built themselves over static-and-existing older signals.
This made sense dependency-wise, because a Memo function would always carry the same set of dependencies.
But as a consequence, implementing basic collection-based dynamic signal, such as that of a "To-Do list" (i.e. an Accessor<Array<Accessor<TodoInterface>>>), was incredibly dificult.

With this PR, dynamic signals based on Sets or Arrays become easy to implement using the library provided UnisetSignal_Factory and ListSignal_Factory exports.

future:

In the future, I hope to add two more dynamic collections (in the feature/dynamic-signals-2 branch):

  • MapCollection<K, V extends Accessor<any>> and its complementary MapSignal<K, V extends Accessor<any>> for holding Accessors in a Map based datastructure.
  • ObjectCollection<SCHEMA> and its complementary ObjectSignal<SCHEMA>, where SCHEMA is an interface with any key: K extends PropertyKey, and value: V extends Accessor<any> pairs.

…lection_signal.test.ts`.

  - this will contain collection type signals, which will be dynamic in nature (i.e. have the ability to add and remove dependencies dynamically).
- for now, only one kind of dynamic collection is implemented: the  `UnisetStateSignal`, which will be able to hold a dynamic set-collection of other signal accessors.
  - the criteria for this signal to fire an update is as follows:
    - fire independently (i.e. as a source node in the DAG) when new accessors are added via its `addItems` method.
    - fire independently when existing accessors in the collection are deleted via its `delItems` method.
    - continue the propagation of a fired signal from one or more dependencies (regular signal DAG behavior). no equality check is performed, since the same set is used for updating the `this.value` of the UnisetStateSignal.
the new pattern looks more feasible, as it:
- separates the logic between:
  - when the signal is to be fired independently due to a mutation in the underlying data structure that is carrying the collection of `Accessor`s. (this is what the `Uniset` class does)
  - when the signal needs to be fired as a result of one of its dependencies changing. (this is what the `UnisetSignal` class does)
- moreover, it allows us to extend the data-structure which we wish to remix directly.
  thus allowing us to override super class methods, and also call `super` methods.
  it becomes far more clear this way which methods result in a potential mutation and will need to initiate the firing of an update cycle.
- finally, in the `static create` method of the signal, it allows us to simply return the entire proxy-like data structure as a single item, rather than returning an array of bound closure methods.
  the advantage of this is that data structures with a ton of mutation methods (like `Array`s, with `push`, `pop`, `shift`, etc...) will not need to have all of their methods first bound, and then returned somewhere in a very long array of bound methods.
  it is simply unsustainable.
- update the complementary test file to use the new signature of the `static create` signal method.
…ctionSignal` return class), that can be extended to adapt to any collection like signal.

- make `UnisetSignal` extend `CollectionSignal`.
- rename the `item` member in `UnisetSignal` to `data`.
- change the fields needed in `BoundSignalInfo`.
  now it uses the `ctx: Context` as is, instead of using its individual member functions.
  it might have very minor performance and minifiablility implications, but it allows the proxy-like accessor collection data structures to use anything available by the context.
…ists (capable of in-between deletion and insertion).

- implement an `RcList` that reference counts the multiplicity (amount of duplicates) of each unique element inside of the list array.
  this class is needed by a list-style collection signal, so that it can know when the dependency on a certain child-accessor has ended, so that it can delete the gaph edge.
  on the other hand, it will also allow us to seamlessly register new dependencies (when their reference counting (`rc`) comes up as 0).
  this signal holds an array-like collection of `Accessor` signals, and takes care of reference counting to deallocate (remove dependency) former accessor elements that are no longer present anywhere in the array.
- change the naming convention of base data structures that are promoted into signals, to contain a "Collection" appended to their name.
  - so, by this convention, the previous `Uniset` class is now called `UnisetCollection`.
  - and the newly introduced `RcList` based data structure is called `ListCollection`.
- in the `List` class:
  - add methods for: `swap`ing elements, and `map`ing and `flatMap`ing to an `Array`.
  - fix a bug in the `insert` method which resulted in wrong indexing of the inserted item when using negative indices.
    previously: `list.insert(-1, item)` would place the `item` in the second to last element.
    but now: `list.insert(-1, item)` will correctly place it in the last spot (similar to pushing).
- in `RcList`, fix a bug (formerly intentional) where reference counting is incremented before pushing/unshifting.
  now, it is the other way around (pushing comes first, then reference counting is incremented), in order to:
  - stay consistent with the remaining method's order of actions taken.
  - allows us to overload the `incRcs` method in subclass, knowing that the item _has_ been added already.
  this design makes it far more easier for the `ListCollection` subclass to react (fire an update cycle), with the most up to date array information (i.e. the update contains the newly added elements. but it wouldn't have if the actions taken were in reverse order, similar to the old way).
- add a test file `/test/collections_signal.list.test.ts` to test the newly added `ListSignal`.
- rename the test file `/test/collections_signal.test.ts` to `/test/collections_signal.uniset.test.ts`, since it is only intended to test the `UnisetSignal` signal class.
@omar-azmi
Copy link
Owner Author

Sugoii!!
I am the President of the United States of USA of America, and I approve of this message.
THIS IS MGGA (make git greate again) COUNTRY!!
All PRs within the border are legal.
All PRs outside the border are illegal.
Throughout the country and its border, Trump alone is the legal one.

@omar-azmi omar-azmi merged commit a82de33 into main Jun 17, 2024
omar-azmi added a commit that referenced this pull request Jun 17, 2024
other changes:
- export the newly added dynamic collection signals #9 in `/src/collection_signal.ts`, to `/src/mod.ts` and `/deno.json`.
- in `/readme.md`, fix a missing directed edge arrow (F -> I) in the mermaid diagram.
  this was pointed out to me by Abu.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant