nadako / TinkStateSharp

Handle those pesky states, now in C#
https://nadako.github.io/TinkStateSharp/
The Unlicense
43 stars 1 forks source link

Explore explicit invalidation idea #21

Open nadako opened 1 year ago

nadako commented 1 year ago

It might be useful to have a State of a mutable object (like a ZString) with a possibility to explicitly invalidate and notify all bindings/auto-observables without changing the Value itself.

This would be similar to how ObservableList/ObservableDictionary work.

Gotta play with the idea and think of the best way to make it play nice with comparators.

nadako commented 1 year ago

The easiest solution would be to have a ForceInvalidate method that unconditionally calls Fire and expose the NeverEqualityComparer, so people would do something like:

var mutableObject = new MutableObject();
var state = Observable.State(mutableObject, NeverEqualityComparer<MutableObject>.Instance);

void Mutate(string newValue)
{
  mutableObject.Field = newValue;
  state.ForceInvalidate();
}

Gotta medidate on this some more to figure out whether it's a good enough API or we need some extra complications.

nadako commented 1 year ago

Hmm, alternatively we could have a separate API, like ManualState<T> or something, that would not use a comparer (or use NeverEqualityComparer, and would not support a custom one) and provide the manual invalidation method. it would extend State<T>

nadako commented 1 year ago

The original tink_state equivalent for this would actually be the SignalObservable - a kind of observable, created from a current value getter and the invalidation signal. This is also something we could consider here, since it's very universal. But, since we don't really have signals in basic C#/.NET and we want to minimize allocations, our implementation of this idea might end up being notably different. Gotta think about this some more.

nadako commented 1 year ago

One more API idea that, I think, is pretty in spirit of .NET is to have a "source" interface with mutation methods that exposes itself as the read-only observable. Kinda similar to how it works with CancellationTokens and tasks.

E.g. something like this (already works in my quick prototype).

public interface ManualObservableSource<T>
{
    Observable<T> Observe();
    void Invalidate();
    void Update(T newValue);
}