lorents / rtl

RTL - Reactive Template Library for C++
MIT License
4 stars 0 forks source link

Introduce rtl::set<T>, rtl::populator #1

Open lorents opened 3 years ago

lorents commented 3 years ago

rtl::set<T> would be an observable set, with operators like

rtl::populator would be a sibling of rtl::animator where each new element is transformed into a collectable resource which is collected when removed.

#include <rtl/set.h>
#include <rtl/populator.h>
...

rtl::set<int> foo = { 0, 1, 2 };
rtl::set<int> bar = { 2, 3, 0 };
rtl::set<int> foobar = foo | bar;

rtl::set<int> baz = foobar
    .filter([](const int& i) { return i > 0; })
    .map<int>([](const int& i) { return i * 2; });

rtl::animator printer = { [&]() 
{
    cout << "assert(foobar == { ";
    for (auto it = foobar.begin(); it != foobar.end(); ++it)
    {
        cout << (*it);
        if(it != foobar.last()) cout << ", ";
    }
    cout << " });" << endl;
}};

rtl::populator pop = foobar.populate([&](const string& s)
{
    cout << "// " << s << " added to foobar" << endl;
    return [s]()
    {
        cout << "//" << s << " removed from foobar" << endl;
    };
});

cout << "foo.add(4);" << endl;
foo.add(4);
cout << "foo.remove(0);" << endl;
foo.remove(0);

cout << "bar.add(2);" << endl;
bar.add(2);
cout << "bar.remove(1);" << endl;
bar.remove(1);

There is also no reason not to support modifying sets that are already collections. this will behave like a modifier on the previous value.

foobar.add(7);
foobar.remove(2);

would really be the same as

foobar = foobar | { 7 };
foobar = foobar - { 2 };

Further, rtl::populator would make sense to work with rtl::var<T> too, behaving like a list with a single element

lorents commented 1 year ago

Status update:

My motivation for introducing rtl::set<T> was to build an observable vector on top of it, thinking this was a good stepping stone towards that. However, during a moment of clarity i managed to implement the observable vector on its own (dubbed rtl::vec<T>).

Turns out we didn't even need the rtl::populator at all, instead there is a reactive rtl::vec<T>::poll(rtl::iterator) method that gives you the list events since the last time it was called with that iterator instance; so you just call poll() inside a regular rtl::animator and do something with the insert/remove events you get back from that.

I plan to push this as soon as i have some more tests for it, but it seems to work.

I have implemented map(), fmap() and filter() for this vec<T>, but the functions passed to those are not reactive. E.g. if the result of the condition passed to filter() changes, the vector would not change, which is a bummer (but good for memory usage if you don't need that). There will be corresponding rmap(), rfilter() and rfmap() methods though that actually change the list if a dependency of the function passed to them changes.