using System.Threading.Tasks;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Collections.Immutable;
Console.WriteLine("Start...");
var myDemoState = new MyDemoState_State();
myDemoState.GetState(DateTimeOffset.Now)
.Subscribe(it => Console.WriteLine($"New state: {string.Join(',', it)}"));
ActionStuff.MakeAction<ImmutableList<MyDemoState>>(it => it.Add(new MyDemoState(2, "World")))
.ToTimestamped(DateTimeOffset.Now + TimeSpan.FromMinutes(1))
ActionStuff.MakeAction<MyDemoState>(it => it with { Name = "Big " + it.Name })
.ToListAction(it => it.Id == 2)
.ToTimestamped(DateTimeOffset.Now + TimeSpan.FromMinutes(1))
ActionStuff.MakeAction<MyDemoState>(it => it with { Name = "Small " + it.Name })
.ToListAction(it => it.Id == 2)
.ToTimestamped(DateTimeOffset.Now - TimeSpan.FromMinutes(1))
Console.WriteLine("Done.");
public interface IAction<T>
public interface ITimestampedAction<T> : IAction<T>
DateTimeOffset Timestamp { get; }
public static class ActionStuff
private class FuncAction<T>(Func<T,T> action) : IAction<T>
public T Apply(T state) => action(state);
private class TimestampedActionWrapper<T>(DateTimeOffset timestamp, IAction<T> action) : ITimestampedAction<T>
public DateTimeOffset Timestamp => timestamp;
public T Apply(T state) => action.Apply(state);
private class ListActionWrapper<T>(IAction<T> action, Func<T, bool> selector) : IAction<ImmutableList<T>>
public ImmutableList<T> Apply(ImmutableList<T> list) =>
.Select(it => selector(it) ? action.Apply(it) : it)
public static IAction<T> MakeAction<T>(Func<T,T> action) => new FuncAction<T>(action);
public static ITimestampedAction<T> ToTimestamped<T>(this IAction<T> action, DateTimeOffset timestamp) => new TimestampedActionWrapper<T>(timestamp, action);
public static IAction<ImmutableList<T>> ToListAction<T>(this IAction<T> action, Func<T, bool> selector) => new ListActionWrapper<T>(action, selector);
public abstract class State<T>
private Subject<ITimestampedAction<T>> _actions = new();
public void Emit(ITimestampedAction<T> action) => _actions.OnNext(action);
public abstract Task<T> InitialState(DateTimeOffset sinceTimestamp);
public IObservable<T> GetState(DateTimeOffset sinceTimestamp) =>
Observable.FromAsync(() => InitialState(sinceTimestamp))
.Select(initial => _actions
.Where(it => it.Timestamp > sinceTimestamp)
.Scan(initial, (acc, it) => it.Apply(acc))
public record MyDemoState(int Id, string Name);
public class MyDemoState_State : State<ImmutableList<MyDemoState>>
public override async Task<ImmutableList<MyDemoState>> InitialState(DateTimeOffset sinceTimestamp) => [new MyDemoState(1, $"Hello")];