From 1b3573c1809b179d40f0b53658a9fe1bb4be43e5 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 13 Mar 2023 23:11:45 -0400 Subject: [PATCH 01/42] Target .NET 7 --- Cycle.NET.Demo/Cycle.NET.Demo.csproj | 2 +- Cycle.NET/Cycle.NET.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cycle.NET.Demo/Cycle.NET.Demo.csproj b/Cycle.NET.Demo/Cycle.NET.Demo.csproj index 894227f..3254220 100644 --- a/Cycle.NET.Demo/Cycle.NET.Demo.csproj +++ b/Cycle.NET.Demo/Cycle.NET.Demo.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + net7.0 diff --git a/Cycle.NET/Cycle.NET.csproj b/Cycle.NET/Cycle.NET.csproj index e7aa633..44b3457 100644 --- a/Cycle.NET/Cycle.NET.csproj +++ b/Cycle.NET/Cycle.NET.csproj @@ -1,7 +1,7 @@ - netstandard2.0 + net7.0 From e3e2b1dea4869b94bd07dcfbc5b57dc376419ee4 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 13 Mar 2023 23:14:18 -0400 Subject: [PATCH 02/42] Install sealed unions --- Cycle.NET/Cycle.NET.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cycle.NET/Cycle.NET.csproj b/Cycle.NET/Cycle.NET.csproj index 44b3457..ce9389a 100644 --- a/Cycle.NET/Cycle.NET.csproj +++ b/Cycle.NET/Cycle.NET.csproj @@ -5,7 +5,8 @@ - + + From 3a6c7634382d5e5e22dd8bc10070c4620f6b683b Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Tue, 14 Mar 2023 22:13:25 -0400 Subject: [PATCH 03/42] Finish initial Runner3 --- Cycle.NET/Runner3.cs | 78 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Cycle.NET/Runner3.cs diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs new file mode 100644 index 0000000..27d3e7e --- /dev/null +++ b/Cycle.NET/Runner3.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Linq; +using SdgApps.Common.DotnetSealedUnions; +using SdgApps.Common.DotnetSealedUnions.Generic; + +namespace Cycle.NET +{ + public class Streams : List, IObservable, IObservable>> + { + public Streams() : base() { } + public Streams(List, IObservable, IObservable>> value) : base(value) { } + } + + public class Drivers + { + public Drivers( + Func, IObservable> onFirst, + Func, IObservable> onSecond, + Func, IObservable> onThird) + { + OnFirst = onFirst; + OnSecond = onSecond; + OnThird = onThird; + } + + public Func, IObservable> OnFirst { get; } + public Func, IObservable> OnSecond { get; } + public Func, IObservable> OnThird { get; } + } + + public static class Runner + { + public static void Run( + Func, Streams> main, + Drivers drivers) + { + // Create fake sinks to use to call the drivers to get around the interdependency between + // main and the drivers. + var sinksFactory = GenericUnions.TripletFactory, ISubject, ISubject>(); + var fakeSinks = new List, ISubject, ISubject>> + { + sinksFactory.First(new ReplaySubject()), + sinksFactory.Second(new ReplaySubject()), + sinksFactory.Third(new ReplaySubject()), + }; + + // Call the drivers and collate the returned sources. + var sourcesFactory = GenericUnions.TripletFactory, IObservable, IObservable>(); + var sources = fakeSinks.Select(sink => sink.Join( + s1s => sourcesFactory.First(drivers.OnFirst(s1s)), + s2s => sourcesFactory.Second(drivers.OnSecond(s2s)), + s3s => sourcesFactory.Third(drivers.OnThird(s3s)))); + + Streams sinks = main(new Streams(sources.ToList())); + + // Update the sinks returned from main with the sinks used by the drivers. + foreach (var sink in sinks) + { + sink.Join( + mapFirst: s1 => s1.Subscribe(s => fakeSinks.SelectMany(f => f.Join( + mapFirst: f1 => new List> { f1 }, + mapSecond: f2 => Enumerable.Empty>(), + mapThird: f3 => Enumerable.Empty>())).Single().OnNext(s)), + mapSecond: s2 => s2.Subscribe(s => fakeSinks.SelectMany(f => f.Join( + mapFirst: f1 => Enumerable.Empty>(), + mapSecond: f2 => new List> { f2 }, + mapThird: f3 => Enumerable.Empty>())).Single().OnNext(s)), + mapThird: s3 => s3.Subscribe(s => fakeSinks.SelectMany(f => f.Join( + mapFirst: f1 => Enumerable.Empty>(), + mapSecond: f2 => Enumerable.Empty>(), + mapThird: f3 => new List> { f3 })).Single().OnNext(s))); + } + } + } +} From 8af41a7cbe73f501b5b195cb4341d6587a5b9a14 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Tue, 14 Mar 2023 22:23:27 -0400 Subject: [PATCH 04/42] Test Runner3 --- Cycle.NET.Demo/Program.cs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 0d7e7b1..a64cd2a 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,40 +1,47 @@ using System; using System.Collections.Generic; +using System.Reactive; using System.Reactive.Linq; +using SdgApps.Common.DotnetSealedUnions.Generic; +using System.Reactive.Subjects; +using System.Linq; namespace Cycle.NET.Demo { class Program { - private static IObservable LogDriver(IObservable sinks) + private static IObservable LogDriver(IObservable sinks) { - sinks.Cast().Subscribe(i => Console.WriteLine("Log " + i.ToString())); + sinks.Subscribe(i => Console.WriteLine("Log " + i.ToString())); - return Observable.Empty(); + return Observable.Empty(); } - private static IObservable KeyInputDriver(IObservable sinks) + private static IObservable KeyInputDriver(IObservable sinks) { - return Observable.Range(20, 5).Select(i => (object)i); + return Observable.Range(20, 5); } - private static Streams CycleMain(Streams sources) + private static Streams CycleMain(Streams sources) { - var sinks = new Streams + var sinksFactory = GenericUnions.TripletFactory, IObservable, IObservable>(); + var sinks = new Streams { - { "log", sources["keys"] } + sinksFactory.First(sources.SelectMany(s => s.Join( + mapFirst: s1 => Enumerable.Empty>(), + mapSecond: s2 => new List> { s2 }, + mapThird: s3 => Enumerable.Empty>())).Single()), }; return sinks; } static void Main(string[] args) { - var drivers = new Drivers - { - { "log", LogDriver }, - { "keys", KeyInputDriver } - }; - Runner.Run(CycleMain, drivers); + var drivers = new Drivers( + onFirst: LogDriver, + onSecond: KeyInputDriver, + onThird: _ => _); + Runner.Run(CycleMain, drivers); Console.ReadLine(); } } From b9fe928d6cac10c72004522df30244828921ab89 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Tue, 14 Mar 2023 23:36:18 -0400 Subject: [PATCH 05/42] Simplify implementation --- Cycle.NET.Demo/Program.cs | 16 +++------ Cycle.NET/Runner3.cs | 76 +++++++++++++++++++++------------------ 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index a64cd2a..3af2491 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; using System.Reactive; using System.Reactive.Linq; -using SdgApps.Common.DotnetSealedUnions.Generic; -using System.Reactive.Subjects; -using System.Linq; namespace Cycle.NET.Demo { @@ -24,14 +20,10 @@ private static IObservable KeyInputDriver(IObservable sinks) private static Streams CycleMain(Streams sources) { - var sinksFactory = GenericUnions.TripletFactory, IObservable, IObservable>(); - var sinks = new Streams - { - sinksFactory.First(sources.SelectMany(s => s.Join( - mapFirst: s1 => Enumerable.Empty>(), - mapSecond: s2 => new List> { s2 }, - mapThird: s3 => Enumerable.Empty>())).Single()), - }; + var sinks = new Streams( + sources.Second, + Observable.Empty(), + Observable.Empty()); return sinks; } diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs index 27d3e7e..2183350 100644 --- a/Cycle.NET/Runner3.cs +++ b/Cycle.NET/Runner3.cs @@ -1,17 +1,24 @@ using System; -using System.Collections.Generic; using System.Reactive.Linq; using System.Reactive.Subjects; -using System.Linq; -using SdgApps.Common.DotnetSealedUnions; -using SdgApps.Common.DotnetSealedUnions.Generic; namespace Cycle.NET { - public class Streams : List, IObservable, IObservable>> + public class Streams { - public Streams() : base() { } - public Streams(List, IObservable, IObservable>> value) : base(value) { } + public Streams( + IObservable first, + IObservable second, + IObservable third) + { + First = first; + Second = second; + Third = third; + } + + public IObservable First { get; } + public IObservable Second { get; } + public IObservable Third { get; } } public class Drivers @@ -39,40 +46,41 @@ public static void Run( { // Create fake sinks to use to call the drivers to get around the interdependency between // main and the drivers. - var sinksFactory = GenericUnions.TripletFactory, ISubject, ISubject>(); - var fakeSinks = new List, ISubject, ISubject>> - { - sinksFactory.First(new ReplaySubject()), - sinksFactory.Second(new ReplaySubject()), - sinksFactory.Third(new ReplaySubject()), - }; + var fakeSinks = new FakeSinks( + new ReplaySubject(), + new ReplaySubject(), + new ReplaySubject()); // Call the drivers and collate the returned sources. - var sourcesFactory = GenericUnions.TripletFactory, IObservable, IObservable>(); - var sources = fakeSinks.Select(sink => sink.Join( - s1s => sourcesFactory.First(drivers.OnFirst(s1s)), - s2s => sourcesFactory.Second(drivers.OnSecond(s2s)), - s3s => sourcesFactory.Third(drivers.OnThird(s3s)))); + var sources = new Streams( + drivers.OnFirst(fakeSinks.First), + drivers.OnSecond(fakeSinks.Second), + drivers.OnThird(fakeSinks.Third)); - Streams sinks = main(new Streams(sources.ToList())); + Streams sinks = main(sources); // Update the sinks returned from main with the sinks used by the drivers. - foreach (var sink in sinks) + sinks.First.Subscribe(fakeSinks.First.OnNext); + sinks.Second.Subscribe(fakeSinks.Second.OnNext); + sinks.Third.Subscribe(fakeSinks.Third.OnNext); + } + + private class FakeSinks + { + public FakeSinks( + ISubject first, + ISubject second, + ISubject third) { - sink.Join( - mapFirst: s1 => s1.Subscribe(s => fakeSinks.SelectMany(f => f.Join( - mapFirst: f1 => new List> { f1 }, - mapSecond: f2 => Enumerable.Empty>(), - mapThird: f3 => Enumerable.Empty>())).Single().OnNext(s)), - mapSecond: s2 => s2.Subscribe(s => fakeSinks.SelectMany(f => f.Join( - mapFirst: f1 => Enumerable.Empty>(), - mapSecond: f2 => new List> { f2 }, - mapThird: f3 => Enumerable.Empty>())).Single().OnNext(s)), - mapThird: s3 => s3.Subscribe(s => fakeSinks.SelectMany(f => f.Join( - mapFirst: f1 => Enumerable.Empty>(), - mapSecond: f2 => Enumerable.Empty>(), - mapThird: f3 => new List> { f3 })).Single().OnNext(s))); + First = first; + Second = second; + Third = third; } + + public ISubject First { get; } + public ISubject Second { get; } + public ISubject Third { get; } } + } } From 9a486ac5bfcccc6f4baa0a7af180a5b0a95dae70 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Thu, 16 Mar 2023 23:04:10 -0400 Subject: [PATCH 06/42] Wrap main delegate --- Cycle.NET.Demo/Program.cs | 4 +++- Cycle.NET/Runner3.cs | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 3af2491..15b7757 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -29,11 +29,13 @@ private static Streams CycleMain(Streams sources } static void Main(string[] args) { + var component = new Component( + CycleMain); var drivers = new Drivers( onFirst: LogDriver, onSecond: KeyInputDriver, onThird: _ => _); - Runner.Run(CycleMain, drivers); + Runner.Run(component, drivers); Console.ReadLine(); } } diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs index 2183350..585d973 100644 --- a/Cycle.NET/Runner3.cs +++ b/Cycle.NET/Runner3.cs @@ -21,6 +21,17 @@ public Streams( public IObservable Third { get; } } + public class Component + { + public Component( + Func, Streams> main) + { + Main = main; + } + + public Func, Streams> Main { get; } + } + public class Drivers { public Drivers( @@ -41,7 +52,7 @@ public Drivers( public static class Runner { public static void Run( - Func, Streams> main, + Component component, Drivers drivers) { // Create fake sinks to use to call the drivers to get around the interdependency between @@ -57,7 +68,7 @@ public static void Run( drivers.OnSecond(fakeSinks.Second), drivers.OnThird(fakeSinks.Third)); - Streams sinks = main(sources); + Streams sinks = component.Main(sources); // Update the sinks returned from main with the sinks used by the drivers. sinks.First.Subscribe(fakeSinks.First.OnNext); From 12a300d17a8f8044dced5bc31525634c89d8404b Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Wed, 22 Mar 2023 20:38:09 -0400 Subject: [PATCH 07/42] Attempt ungeneric interface Unfortunately this is little better than IRunner.Run --- Cycle.NET.Demo/Program.cs | 140 ++++++++++++++++++++++++++++++++++++-- Cycle.NET/Runner3.cs | 84 +++++------------------ 2 files changed, 152 insertions(+), 72 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 15b7757..dc7c2ea 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,10 +1,143 @@ using System; using System.Reactive; using System.Reactive.Linq; +using System.Reactive.Subjects; namespace Cycle.NET.Demo { - class Program + public class Streams + { + public Streams( + IObservable first, + IObservable second, + IObservable third) + { + First = first; + Second = second; + Third = third; + } + + public IObservable First { get; } + public IObservable Second { get; } + public IObservable Third { get; } + } + + public class Component : IComponent + { + public Component( + FakeSinks fakeSinks, + IObservable first, + IObservable second, + IObservable third) + { + FakeSinks = fakeSinks; + First = first; + Second = second; + Third = third; + } + + public FakeSinks FakeSinks { get; } + public IObservable First { get; } + public IObservable Second { get; } + public IObservable Third { get; } + + public ISinks Main() + { + var sources = new Streams(First, Second, Third); + var sinks = FakeSinks.Drivers.Main(sources); + return new Sinks( + this, + sinks.First, + sinks.Second, + sinks.Third); + } + } + + internal class Sinks : ISinks + { + public Sinks(Component component, IObservable first, IObservable second, IObservable third) + { + Component = component; + First = first; + Second = second; + Third = third; + } + + public Component Component { get; } + public IObservable First { get; } + public IObservable Second { get; } + public IObservable Third { get; } + + public void Subscribe() + { + First.Subscribe(Component.FakeSinks.First.OnNext); + Second.Subscribe(Component.FakeSinks.Second.OnNext); + Third.Subscribe(Component.FakeSinks.Third.OnNext); + } + } + + public class Drivers : IDrivers + { + public Drivers( + Func, Streams> main, + Func, IObservable> onFirst, + Func, IObservable> onSecond, + Func, IObservable> onThird) + { + Main = main; + OnFirst = onFirst; + OnSecond = onSecond; + OnThird = onThird; + } + + public Func, Streams> Main { get; } + public Func, IObservable> OnFirst { get; } + public Func, IObservable> OnSecond { get; } + public Func, IObservable> OnThird { get; } + + public IFakeSinks CreateFakeSinks() + { + return new FakeSinks( + this, + new ReplaySubject(), + new ReplaySubject(), + new ReplaySubject()); + } + } + + public class FakeSinks : IFakeSinks + { + public FakeSinks( + Drivers drivers, + ISubject first, + ISubject second, + ISubject third) + { + Drivers = drivers; + First = first; + Second = second; + Third = third; + } + + public Drivers Drivers { get; } + public ISubject First { get; } + public ISubject Second { get; } + public ISubject Third { get; } + + public IComponent Invoke() + { + IObservable first = this.Drivers.OnFirst(this.First); + IObservable second = this.Drivers.OnSecond(this.Second); + IObservable third = this.Drivers.OnThird(this.Third); + return new Component( + this, + first, + second, + third); + } + } + + static class Program { private static IObservable LogDriver(IObservable sinks) { @@ -29,13 +162,12 @@ private static Streams CycleMain(Streams sources } static void Main(string[] args) { - var component = new Component( - CycleMain); var drivers = new Drivers( + CycleMain, onFirst: LogDriver, onSecond: KeyInputDriver, onThird: _ => _); - Runner.Run(component, drivers); + Runner.Run(drivers); Console.ReadLine(); } } diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs index 585d973..e974cba 100644 --- a/Cycle.NET/Runner3.cs +++ b/Cycle.NET/Runner3.cs @@ -4,94 +4,42 @@ namespace Cycle.NET { - public class Streams + public interface IComponent { - public Streams( - IObservable first, - IObservable second, - IObservable third) - { - First = first; - Second = second; - Third = third; - } - - public IObservable First { get; } - public IObservable Second { get; } - public IObservable Third { get; } + ISinks Main(); } - public class Component + public interface ISinks { - public Component( - Func, Streams> main) - { - Main = main; - } - - public Func, Streams> Main { get; } + void Subscribe(); } - public class Drivers + public interface IDrivers { - public Drivers( - Func, IObservable> onFirst, - Func, IObservable> onSecond, - Func, IObservable> onThird) - { - OnFirst = onFirst; - OnSecond = onSecond; - OnThird = onThird; - } + IFakeSinks CreateFakeSinks(); + } - public Func, IObservable> OnFirst { get; } - public Func, IObservable> OnSecond { get; } - public Func, IObservable> OnThird { get; } + public interface IFakeSinks + { + IComponent Invoke(); } - public static class Runner + public static class Runner { public static void Run( - Component component, - Drivers drivers) + IDrivers drivers) { // Create fake sinks to use to call the drivers to get around the interdependency between // main and the drivers. - var fakeSinks = new FakeSinks( - new ReplaySubject(), - new ReplaySubject(), - new ReplaySubject()); + var fakeSinks = drivers.CreateFakeSinks(); // Call the drivers and collate the returned sources. - var sources = new Streams( - drivers.OnFirst(fakeSinks.First), - drivers.OnSecond(fakeSinks.Second), - drivers.OnThird(fakeSinks.Third)); + var sources = fakeSinks.Invoke(); - Streams sinks = component.Main(sources); + var sinks = sources.Main(); // Update the sinks returned from main with the sinks used by the drivers. - sinks.First.Subscribe(fakeSinks.First.OnNext); - sinks.Second.Subscribe(fakeSinks.Second.OnNext); - sinks.Third.Subscribe(fakeSinks.Third.OnNext); + sinks.Subscribe(); } - - private class FakeSinks - { - public FakeSinks( - ISubject first, - ISubject second, - ISubject third) - { - First = first; - Second = second; - Third = third; - } - - public ISubject First { get; } - public ISubject Second { get; } - public ISubject Third { get; } - } - } } From 890f37733ee8e93ecd038d2c8d3ac48cddce9576 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Wed, 22 Mar 2023 20:38:20 -0400 Subject: [PATCH 08/42] Revert "Attempt ungeneric interface" This reverts commit 12a300d17a8f8044dced5bc31525634c89d8404b. --- Cycle.NET.Demo/Program.cs | 140 ++------------------------------------ Cycle.NET/Runner3.cs | 84 ++++++++++++++++++----- 2 files changed, 72 insertions(+), 152 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index dc7c2ea..15b7757 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,143 +1,10 @@ using System; using System.Reactive; using System.Reactive.Linq; -using System.Reactive.Subjects; namespace Cycle.NET.Demo { - public class Streams - { - public Streams( - IObservable first, - IObservable second, - IObservable third) - { - First = first; - Second = second; - Third = third; - } - - public IObservable First { get; } - public IObservable Second { get; } - public IObservable Third { get; } - } - - public class Component : IComponent - { - public Component( - FakeSinks fakeSinks, - IObservable first, - IObservable second, - IObservable third) - { - FakeSinks = fakeSinks; - First = first; - Second = second; - Third = third; - } - - public FakeSinks FakeSinks { get; } - public IObservable First { get; } - public IObservable Second { get; } - public IObservable Third { get; } - - public ISinks Main() - { - var sources = new Streams(First, Second, Third); - var sinks = FakeSinks.Drivers.Main(sources); - return new Sinks( - this, - sinks.First, - sinks.Second, - sinks.Third); - } - } - - internal class Sinks : ISinks - { - public Sinks(Component component, IObservable first, IObservable second, IObservable third) - { - Component = component; - First = first; - Second = second; - Third = third; - } - - public Component Component { get; } - public IObservable First { get; } - public IObservable Second { get; } - public IObservable Third { get; } - - public void Subscribe() - { - First.Subscribe(Component.FakeSinks.First.OnNext); - Second.Subscribe(Component.FakeSinks.Second.OnNext); - Third.Subscribe(Component.FakeSinks.Third.OnNext); - } - } - - public class Drivers : IDrivers - { - public Drivers( - Func, Streams> main, - Func, IObservable> onFirst, - Func, IObservable> onSecond, - Func, IObservable> onThird) - { - Main = main; - OnFirst = onFirst; - OnSecond = onSecond; - OnThird = onThird; - } - - public Func, Streams> Main { get; } - public Func, IObservable> OnFirst { get; } - public Func, IObservable> OnSecond { get; } - public Func, IObservable> OnThird { get; } - - public IFakeSinks CreateFakeSinks() - { - return new FakeSinks( - this, - new ReplaySubject(), - new ReplaySubject(), - new ReplaySubject()); - } - } - - public class FakeSinks : IFakeSinks - { - public FakeSinks( - Drivers drivers, - ISubject first, - ISubject second, - ISubject third) - { - Drivers = drivers; - First = first; - Second = second; - Third = third; - } - - public Drivers Drivers { get; } - public ISubject First { get; } - public ISubject Second { get; } - public ISubject Third { get; } - - public IComponent Invoke() - { - IObservable first = this.Drivers.OnFirst(this.First); - IObservable second = this.Drivers.OnSecond(this.Second); - IObservable third = this.Drivers.OnThird(this.Third); - return new Component( - this, - first, - second, - third); - } - } - - static class Program + class Program { private static IObservable LogDriver(IObservable sinks) { @@ -162,12 +29,13 @@ private static Streams CycleMain(Streams sources } static void Main(string[] args) { + var component = new Component( + CycleMain); var drivers = new Drivers( - CycleMain, onFirst: LogDriver, onSecond: KeyInputDriver, onThird: _ => _); - Runner.Run(drivers); + Runner.Run(component, drivers); Console.ReadLine(); } } diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs index e974cba..585d973 100644 --- a/Cycle.NET/Runner3.cs +++ b/Cycle.NET/Runner3.cs @@ -4,42 +4,94 @@ namespace Cycle.NET { - public interface IComponent + public class Streams { - ISinks Main(); - } + public Streams( + IObservable first, + IObservable second, + IObservable third) + { + First = first; + Second = second; + Third = third; + } - public interface ISinks - { - void Subscribe(); + public IObservable First { get; } + public IObservable Second { get; } + public IObservable Third { get; } } - public interface IDrivers + public class Component { - IFakeSinks CreateFakeSinks(); + public Component( + Func, Streams> main) + { + Main = main; + } + + public Func, Streams> Main { get; } } - public interface IFakeSinks + public class Drivers { - IComponent Invoke(); + public Drivers( + Func, IObservable> onFirst, + Func, IObservable> onSecond, + Func, IObservable> onThird) + { + OnFirst = onFirst; + OnSecond = onSecond; + OnThird = onThird; + } + + public Func, IObservable> OnFirst { get; } + public Func, IObservable> OnSecond { get; } + public Func, IObservable> OnThird { get; } } - public static class Runner + public static class Runner { public static void Run( - IDrivers drivers) + Component component, + Drivers drivers) { // Create fake sinks to use to call the drivers to get around the interdependency between // main and the drivers. - var fakeSinks = drivers.CreateFakeSinks(); + var fakeSinks = new FakeSinks( + new ReplaySubject(), + new ReplaySubject(), + new ReplaySubject()); // Call the drivers and collate the returned sources. - var sources = fakeSinks.Invoke(); + var sources = new Streams( + drivers.OnFirst(fakeSinks.First), + drivers.OnSecond(fakeSinks.Second), + drivers.OnThird(fakeSinks.Third)); - var sinks = sources.Main(); + Streams sinks = component.Main(sources); // Update the sinks returned from main with the sinks used by the drivers. - sinks.Subscribe(); + sinks.First.Subscribe(fakeSinks.First.OnNext); + sinks.Second.Subscribe(fakeSinks.Second.OnNext); + sinks.Third.Subscribe(fakeSinks.Third.OnNext); } + + private class FakeSinks + { + public FakeSinks( + ISubject first, + ISubject second, + ISubject third) + { + First = first; + Second = second; + Third = third; + } + + public ISubject First { get; } + public ISubject Second { get; } + public ISubject Third { get; } + } + } } From b55ffdc0b694bca70bc3654bb3376776c04f2696 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Wed, 22 Mar 2023 20:41:25 -0400 Subject: [PATCH 09/42] Make Runner1 from Runner3 --- Cycle.NET/Runner1.cs | 73 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Cycle.NET/Runner1.cs diff --git a/Cycle.NET/Runner1.cs b/Cycle.NET/Runner1.cs new file mode 100644 index 0000000..dafc96e --- /dev/null +++ b/Cycle.NET/Runner1.cs @@ -0,0 +1,73 @@ +using System; +using System.Reactive.Linq; +using System.Reactive.Subjects; + +namespace Cycle.NET +{ + public class Streams + { + public Streams( + IObservable first) + { + First = first; + } + + public IObservable First { get; } + } + + public class Component + { + public Component( + Func, Streams> main) + { + Main = main; + } + + public Func, Streams> Main { get; } + } + + public class Drivers + { + public Drivers( + Func, IObservable> onFirst) + { + OnFirst = onFirst; + } + + public Func, IObservable> OnFirst { get; } + } + + public static class Runner + { + public static void Run( + Component component, + Drivers drivers) + { + // Create fake sinks to use to call the drivers to get around the interdependency between + // main and the drivers. + var fakeSinks = new FakeSinks( + new ReplaySubject()); + + // Call the drivers and collate the returned sources. + var sources = new Streams( + drivers.OnFirst(fakeSinks.First)); + + Streams sinks = component.Main(sources); + + // Update the sinks returned from main with the sinks used by the drivers. + sinks.First.Subscribe(fakeSinks.First.OnNext); + } + + private class FakeSinks + { + public FakeSinks( + ISubject first) + { + First = first; + } + + public ISubject First { get; } + } + + } +} From c30ec5875007664e56bf54ca6a62cd38104c68f7 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 27 Mar 2023 20:29:25 -0400 Subject: [PATCH 10/42] Implement demo with obs of tuple --- Cycle.NET.Demo/Program.cs | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 15b7757..b27b47b 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -18,24 +18,35 @@ private static IObservable KeyInputDriver(IObservable sinks) return Observable.Range(20, 5); } - private static Streams CycleMain(Streams sources) + private static IObservable<(int? Log, int? KeyInput)> Driver(IObservable<(int? Log, int? KeyInput)> sinks) { - var sinks = new Streams( - sources.Second, - Observable.Empty(), - Observable.Empty()); + var logSinks = sinks.Where(s => s.Log.HasValue); + var logSources = LogDriver(logSinks.Select(s => s.Log.Value)); + + var keyInputSinks = sinks.Where(s => s.KeyInput.HasValue); + var keyInputSources = KeyInputDriver(keyInputSinks.Select(s => s.KeyInput.Value)); + + return Observable.Merge( + logSources.Select(s => (Log: s as int?, KeyInput: null as int?)), + keyInputSources.Select(s => (Log: null as int?, KeyInput: s as int?))); + } + + private static Streams<(int? Log, int? KeyInput)> CycleMain(Streams<(int? Log, int? KeyInput)> sources) + { + var sinks = new Streams<(int? Log, int? KeyInput)>( + sources.First.Select(s => ( + Log: s.KeyInput, + KeyInput: null as int?))); return sinks; } static void Main(string[] args) { - var component = new Component( + var component = new Component<(int? Log, int? KeyInput), (int? Log, int? KeyInput)>( CycleMain); - var drivers = new Drivers( - onFirst: LogDriver, - onSecond: KeyInputDriver, - onThird: _ => _); - Runner.Run(component, drivers); + var drivers = new Drivers<(int? Log, int? KeyInput), (int? Log, int? KeyInput)>( + onFirst: Driver); + Runner<(int? Log, int? KeyInput), (int? Log, int? KeyInput)>.Run(component, drivers); Console.ReadLine(); } } From ec88a0da7d86c176a76186ec0f62b0aa93c77bcf Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 27 Mar 2023 20:35:51 -0400 Subject: [PATCH 11/42] Remove redundant streams type --- Cycle.NET.Demo/Program.cs | 9 ++++----- Cycle.NET/Runner1.cs | 37 ++++++------------------------------- 2 files changed, 10 insertions(+), 36 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index b27b47b..483aa13 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -31,12 +31,11 @@ private static IObservable KeyInputDriver(IObservable sinks) keyInputSources.Select(s => (Log: null as int?, KeyInput: s as int?))); } - private static Streams<(int? Log, int? KeyInput)> CycleMain(Streams<(int? Log, int? KeyInput)> sources) + private static IObservable<(int? Log, int? KeyInput)> CycleMain(IObservable<(int? Log, int? KeyInput)> sources) { - var sinks = new Streams<(int? Log, int? KeyInput)>( - sources.First.Select(s => ( - Log: s.KeyInput, - KeyInput: null as int?))); + var sinks = sources.Select(s => ( + Log: s.KeyInput, + KeyInput: null as int?)); return sinks; } diff --git a/Cycle.NET/Runner1.cs b/Cycle.NET/Runner1.cs index dafc96e..334b928 100644 --- a/Cycle.NET/Runner1.cs +++ b/Cycle.NET/Runner1.cs @@ -4,26 +4,15 @@ namespace Cycle.NET { - public class Streams - { - public Streams( - IObservable first) - { - First = first; - } - - public IObservable First { get; } - } - public class Component { public Component( - Func, Streams> main) + Func, IObservable> main) { Main = main; } - public Func, Streams> Main { get; } + public Func, IObservable> Main { get; } } public class Drivers @@ -45,29 +34,15 @@ public static void Run( { // Create fake sinks to use to call the drivers to get around the interdependency between // main and the drivers. - var fakeSinks = new FakeSinks( - new ReplaySubject()); + var fakeSinks = new ReplaySubject(); // Call the drivers and collate the returned sources. - var sources = new Streams( - drivers.OnFirst(fakeSinks.First)); + var sources = drivers.OnFirst(fakeSinks); - Streams sinks = component.Main(sources); + IObservable sinks = component.Main(sources); // Update the sinks returned from main with the sinks used by the drivers. - sinks.First.Subscribe(fakeSinks.First.OnNext); + sinks.Subscribe(fakeSinks.OnNext); } - - private class FakeSinks - { - public FakeSinks( - ISubject first) - { - First = first; - } - - public ISubject First { get; } - } - } } From 603d2ab73c7a301a9d7402a8b9dd645f3b358c27 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 27 Mar 2023 21:22:24 -0400 Subject: [PATCH 12/42] Convert tuple to sum type --- Cycle.NET.Demo/Cycle.NET.Demo.csproj | 4 +++ Cycle.NET.Demo/Program.cs | 39 ++++++++++++++++++---------- Cycle.NET/Cycle.NET.csproj | 1 - 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Cycle.NET.Demo/Cycle.NET.Demo.csproj b/Cycle.NET.Demo/Cycle.NET.Demo.csproj index 3254220..6ac46b6 100644 --- a/Cycle.NET.Demo/Cycle.NET.Demo.csproj +++ b/Cycle.NET.Demo/Cycle.NET.Demo.csproj @@ -5,6 +5,10 @@ net7.0 + + + + diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 483aa13..ea9e745 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,6 +1,8 @@ using System; using System.Reactive; using System.Reactive.Linq; +using SdgApps.Common.DotnetSealedUnions; +using SdgApps.Common.DotnetSealedUnions.Generic; namespace Cycle.NET.Demo { @@ -18,34 +20,43 @@ private static IObservable KeyInputDriver(IObservable sinks) return Observable.Range(20, 5); } - private static IObservable<(int? Log, int? KeyInput)> Driver(IObservable<(int? Log, int? KeyInput)> sinks) + private static IObservable> Driver(IObservable> sinks) { - var logSinks = sinks.Where(s => s.Log.HasValue); - var logSources = LogDriver(logSinks.Select(s => s.Log.Value)); + var fac = GenericUnions.DoubletFactory(); - var keyInputSinks = sinks.Where(s => s.KeyInput.HasValue); - var keyInputSources = KeyInputDriver(keyInputSinks.Select(s => s.KeyInput.Value)); + var logSinks = sinks.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty())); + var logSources = LogDriver(logSinks); + + var keyInputSinks = sinks.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return)); + var keyInputSources = KeyInputDriver(keyInputSinks); return Observable.Merge( - logSources.Select(s => (Log: s as int?, KeyInput: null as int?)), - keyInputSources.Select(s => (Log: null as int?, KeyInput: s as int?))); + logSources.Select(fac.First), + keyInputSources.Select(fac.Second)); } - private static IObservable<(int? Log, int? KeyInput)> CycleMain(IObservable<(int? Log, int? KeyInput)> sources) + private static IObservable> CycleMain(IObservable> sources) { - var sinks = sources.Select(s => ( - Log: s.KeyInput, - KeyInput: null as int?)); + var fac = GenericUnions.DoubletFactory(); + + var keyInputSources = sources.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return)); + var sinks = keyInputSources.Select(fac.First); return sinks; } static void Main(string[] args) { - var component = new Component<(int? Log, int? KeyInput), (int? Log, int? KeyInput)>( + var component = new Component, IUnion2>( CycleMain); - var drivers = new Drivers<(int? Log, int? KeyInput), (int? Log, int? KeyInput)>( + var drivers = new Drivers, IUnion2>( onFirst: Driver); - Runner<(int? Log, int? KeyInput), (int? Log, int? KeyInput)>.Run(component, drivers); + Runner, IUnion2>.Run(component, drivers); Console.ReadLine(); } } diff --git a/Cycle.NET/Cycle.NET.csproj b/Cycle.NET/Cycle.NET.csproj index ce9389a..624b282 100644 --- a/Cycle.NET/Cycle.NET.csproj +++ b/Cycle.NET/Cycle.NET.csproj @@ -5,7 +5,6 @@ - From 99a5a47850204a1667a9cafa1803a25031b0242b Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Thu, 30 Mar 2023 22:39:50 -0400 Subject: [PATCH 13/42] Add stream adapters --- Cycle.NET.Demo/Program.cs | 31 +++++++++++---------- Cycle.NET/Cycle.NET.csproj | 1 + Cycle.NET/Extensions/StreamsExtensions.cs | 25 +++++++++++++++++ Cycle.NET/Streams2.cs | 33 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 Cycle.NET/Extensions/StreamsExtensions.cs create mode 100644 Cycle.NET/Streams2.cs diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index ea9e745..a5ebd4b 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,6 +1,7 @@ using System; using System.Reactive; using System.Reactive.Linq; +using Cycle.NET.Extensions; using SdgApps.Common.DotnetSealedUnions; using SdgApps.Common.DotnetSealedUnions.Generic; @@ -22,33 +23,31 @@ private static IObservable KeyInputDriver(IObservable sinks) private static IObservable> Driver(IObservable> sinks) { - var fac = GenericUnions.DoubletFactory(); + var sinkStreams = sinks.ToStreams(); - var logSinks = sinks.SelectMany(s => s.Join( - mapFirst: Observable.Return, - mapSecond: _ => Observable.Empty())); + var logSinks = sinkStreams.First; var logSources = LogDriver(logSinks); - var keyInputSinks = sinks.SelectMany(s => s.Join( - mapFirst: _ => Observable.Empty(), - mapSecond: Observable.Return)); + var keyInputSinks = sinkStreams.Second; var keyInputSources = KeyInputDriver(keyInputSinks); - return Observable.Merge( - logSources.Select(fac.First), - keyInputSources.Select(fac.Second)); + return new Streams( + logSources, + keyInputSources) + .ToStream(); } private static IObservable> CycleMain(IObservable> sources) { - var fac = GenericUnions.DoubletFactory(); + var sourceStreams = sources.ToStreams(); - var keyInputSources = sources.SelectMany(s => s.Join( - mapFirst: _ => Observable.Empty(), - mapSecond: Observable.Return)); - var sinks = keyInputSources.Select(fac.First); + var keyInputSource = sourceStreams.Second; + var logSink = keyInputSource; + var sinkStreams = new Streams( + logSink, + Observable.Empty()); - return sinks; + return sinkStreams.ToStream(); } static void Main(string[] args) { diff --git a/Cycle.NET/Cycle.NET.csproj b/Cycle.NET/Cycle.NET.csproj index 624b282..ce9389a 100644 --- a/Cycle.NET/Cycle.NET.csproj +++ b/Cycle.NET/Cycle.NET.csproj @@ -5,6 +5,7 @@ + diff --git a/Cycle.NET/Extensions/StreamsExtensions.cs b/Cycle.NET/Extensions/StreamsExtensions.cs new file mode 100644 index 0000000..63c05bf --- /dev/null +++ b/Cycle.NET/Extensions/StreamsExtensions.cs @@ -0,0 +1,25 @@ +using System; +using System.Reactive.Linq; +using SdgApps.Common.DotnetSealedUnions; + +namespace Cycle.NET.Extensions +{ + public static class StreamsExtensions + { + public static Streams ToStreams( + this IObservable> source) + { + var first = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty())); + + var second = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return)); + + return new Streams( + first, + second); + } + } +} diff --git a/Cycle.NET/Streams2.cs b/Cycle.NET/Streams2.cs new file mode 100644 index 0000000..20a24cf --- /dev/null +++ b/Cycle.NET/Streams2.cs @@ -0,0 +1,33 @@ +using System; +using System.Reactive.Linq; +using SdgApps.Common.DotnetSealedUnions; +using SdgApps.Common.DotnetSealedUnions.Generic; + +namespace Cycle.NET +{ + public class Streams + { + public Streams( + IObservable first, + IObservable second) + { + First = first; + Second = second; + } + + public IObservable First { get; } + public IObservable Second { get; } + + public IObservable> ToStream() + { + var fac = GenericUnions.DoubletFactory(); + + var first = this.First.Select(fac.First); + var second = this.Second.Select(fac.Second); + + return Observable.Merge( + first, + second); + } + } +} From d6cabf35f4e552079c87148f0c86b1893fef7550 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Sat, 1 Apr 2023 23:30:57 -0400 Subject: [PATCH 14/42] Replace streams with generic tuple --- Cycle.NET.Demo/Program.cs | 24 +++++++------- ...nsions.cs => ObservableUnionExtensions.cs} | 16 +++++---- Cycle.NET/ObservableUnion.cs | 24 ++++++++++++++ Cycle.NET/Streams2.cs | 33 ------------------- 4 files changed, 44 insertions(+), 53 deletions(-) rename Cycle.NET/Extensions/{StreamsExtensions.cs => ObservableUnionExtensions.cs} (53%) create mode 100644 Cycle.NET/ObservableUnion.cs delete mode 100644 Cycle.NET/Streams2.cs diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index a5ebd4b..c0f4146 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -23,31 +23,29 @@ private static IObservable KeyInputDriver(IObservable sinks) private static IObservable> Driver(IObservable> sinks) { - var sinkStreams = sinks.ToStreams(); + var ( + logSinks, + keyInputSinks) = sinks.Split(); - var logSinks = sinkStreams.First; var logSources = LogDriver(logSinks); - - var keyInputSinks = sinkStreams.Second; var keyInputSources = KeyInputDriver(keyInputSinks); - return new Streams( + return ObservableUnion.Merge( logSources, - keyInputSources) - .ToStream(); + keyInputSources); } private static IObservable> CycleMain(IObservable> sources) { - var sourceStreams = sources.ToStreams(); + var ( + _, + keyInputSources) = sources.Split(); + + var logSink = keyInputSources; - var keyInputSource = sourceStreams.Second; - var logSink = keyInputSource; - var sinkStreams = new Streams( + return ObservableUnion.Merge( logSink, Observable.Empty()); - - return sinkStreams.ToStream(); } static void Main(string[] args) { diff --git a/Cycle.NET/Extensions/StreamsExtensions.cs b/Cycle.NET/Extensions/ObservableUnionExtensions.cs similarity index 53% rename from Cycle.NET/Extensions/StreamsExtensions.cs rename to Cycle.NET/Extensions/ObservableUnionExtensions.cs index 63c05bf..c4cf995 100644 --- a/Cycle.NET/Extensions/StreamsExtensions.cs +++ b/Cycle.NET/Extensions/ObservableUnionExtensions.cs @@ -4,22 +4,24 @@ namespace Cycle.NET.Extensions { - public static class StreamsExtensions + public static class ObservableUnionExtensions { - public static Streams ToStreams( + public static ( + IObservable Firsts, + IObservable Seconds) Split( this IObservable> source) { - var first = source.SelectMany(s => s.Join( + var firsts = source.SelectMany(s => s.Join( mapFirst: Observable.Return, mapSecond: _ => Observable.Empty())); - var second = source.SelectMany(s => s.Join( + var seconds = source.SelectMany(s => s.Join( mapFirst: _ => Observable.Empty(), mapSecond: Observable.Return)); - return new Streams( - first, - second); + return ( + Firsts: firsts, + Seconds: seconds); } } } diff --git a/Cycle.NET/ObservableUnion.cs b/Cycle.NET/ObservableUnion.cs new file mode 100644 index 0000000..e6a3060 --- /dev/null +++ b/Cycle.NET/ObservableUnion.cs @@ -0,0 +1,24 @@ +using System; +using System.Reactive.Linq; +using SdgApps.Common.DotnetSealedUnions; +using SdgApps.Common.DotnetSealedUnions.Generic; + +namespace Cycle.NET +{ + public static class ObservableUnion + { + public static IObservable> Merge( + IObservable firsts, + IObservable seconds) + { + var fac = GenericUnions.DoubletFactory(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + + return Observable.Merge( + unionFirsts, + unionSeconds); + } + } +} diff --git a/Cycle.NET/Streams2.cs b/Cycle.NET/Streams2.cs deleted file mode 100644 index 20a24cf..0000000 --- a/Cycle.NET/Streams2.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Reactive.Linq; -using SdgApps.Common.DotnetSealedUnions; -using SdgApps.Common.DotnetSealedUnions.Generic; - -namespace Cycle.NET -{ - public class Streams - { - public Streams( - IObservable first, - IObservable second) - { - First = first; - Second = second; - } - - public IObservable First { get; } - public IObservable Second { get; } - - public IObservable> ToStream() - { - var fac = GenericUnions.DoubletFactory(); - - var first = this.First.Select(fac.First); - var second = this.Second.Select(fac.Second); - - return Observable.Merge( - first, - second); - } - } -} From 7bbb5d9f58e6f9a82bb5b9de039ad15bced8bf94 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 3 Apr 2023 21:07:32 -0400 Subject: [PATCH 15/42] Abstract calling drivers --- Cycle.NET.Demo/Program.cs | 13 ++----- .../Extensions/ObservableUnionExtensions.cs | 35 +++++++++++++++++-- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index c0f4146..c6c7aee 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -23,16 +23,9 @@ private static IObservable KeyInputDriver(IObservable sinks) private static IObservable> Driver(IObservable> sinks) { - var ( - logSinks, - keyInputSinks) = sinks.Split(); - - var logSources = LogDriver(logSinks); - var keyInputSources = KeyInputDriver(keyInputSinks); - - return ObservableUnion.Merge( - logSources, - keyInputSources); + return sinks.CallDrivers( + LogDriver, + KeyInputDriver); } private static IObservable> CycleMain(IObservable> sources) diff --git a/Cycle.NET/Extensions/ObservableUnionExtensions.cs b/Cycle.NET/Extensions/ObservableUnionExtensions.cs index c4cf995..fabcbb0 100644 --- a/Cycle.NET/Extensions/ObservableUnionExtensions.cs +++ b/Cycle.NET/Extensions/ObservableUnionExtensions.cs @@ -8,8 +8,13 @@ public static class ObservableUnionExtensions { public static ( IObservable Firsts, - IObservable Seconds) Split( - this IObservable> source) + IObservable Seconds) + Split< + TFirst, + TSecond>( + this IObservable> source) { var firsts = source.SelectMany(s => s.Join( mapFirst: Observable.Return, @@ -23,5 +28,31 @@ public static ( Firsts: firsts, Seconds: seconds); } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver) + { + var ( + firstSink, + secondSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + + return ObservableUnion.Merge( + firstSource, + secondSource); + } } } From a8747dde5c9cf8fa6c437bd230b526a132cafc8c Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 3 Apr 2023 21:16:03 -0400 Subject: [PATCH 16/42] Move drivers caller --- Cycle.NET.Demo/Program.cs | 12 +++---- Cycle.NET/Drivers.cs | 35 +++++++++++++++++++ .../Extensions/ObservableUnionExtensions.cs | 26 -------------- Cycle.NET/Runner.cs | 2 +- 4 files changed, 40 insertions(+), 35 deletions(-) create mode 100644 Cycle.NET/Drivers.cs diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index c6c7aee..0528fbc 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -21,13 +21,6 @@ private static IObservable KeyInputDriver(IObservable sinks) return Observable.Range(20, 5); } - private static IObservable> Driver(IObservable> sinks) - { - return sinks.CallDrivers( - LogDriver, - KeyInputDriver); - } - private static IObservable> CycleMain(IObservable> sources) { var ( @@ -45,7 +38,10 @@ static void Main(string[] args) var component = new Component, IUnion2>( CycleMain); var drivers = new Drivers, IUnion2>( - onFirst: Driver); + onFirst: sinks => Drivers.Call( + sinks, + LogDriver, + KeyInputDriver)); Runner, IUnion2>.Run(component, drivers); Console.ReadLine(); } diff --git a/Cycle.NET/Drivers.cs b/Cycle.NET/Drivers.cs new file mode 100644 index 0000000..205be4c --- /dev/null +++ b/Cycle.NET/Drivers.cs @@ -0,0 +1,35 @@ +using System; +using Cycle.NET.Extensions; +using SdgApps.Common.DotnetSealedUnions; + +namespace Cycle.NET +{ + public partial class Drivers + { + public static IObservable> + Call< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource>( + IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver) + { + var ( + firstSink, + secondSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + + return ObservableUnion.Merge( + firstSource, + secondSource); + } + } +} diff --git a/Cycle.NET/Extensions/ObservableUnionExtensions.cs b/Cycle.NET/Extensions/ObservableUnionExtensions.cs index fabcbb0..604159e 100644 --- a/Cycle.NET/Extensions/ObservableUnionExtensions.cs +++ b/Cycle.NET/Extensions/ObservableUnionExtensions.cs @@ -28,31 +28,5 @@ this IObservable> - CallDrivers< - TFirstSink, - TFirstSource, - TSecondSink, - TSecondSource>( - this IObservable> sinks, - Func, IObservable> firstDriver, - Func, IObservable> secondDriver) - { - var ( - firstSink, - secondSink) = sinks.Split(); - - var firstSource = firstDriver(firstSink); - var secondSource = secondDriver(secondSink); - - return ObservableUnion.Merge( - firstSource, - secondSource); - } } } diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index 07b668c..c9bb4fb 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -13,7 +13,7 @@ public Streams() : base() { } public Streams(Dictionary> value) : base(value) { } } - public class Drivers : Dictionary, IObservable>> + public partial class Drivers : Dictionary, IObservable>> { public Drivers() : base() { } } From 24de5460e67736f9e1d8ad08c365c3856b55b87f Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 3 Apr 2023 23:17:19 -0400 Subject: [PATCH 17/42] Move driver call again --- Cycle.NET.Demo/Program.cs | 3 +-- Cycle.NET/{Drivers.cs => Extensions/DriverExtensions.cs} | 6 +++--- Cycle.NET/Runner.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) rename Cycle.NET/{Drivers.cs => Extensions/DriverExtensions.cs} (89%) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 0528fbc..1511b06 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -38,8 +38,7 @@ static void Main(string[] args) var component = new Component, IUnion2>( CycleMain); var drivers = new Drivers, IUnion2>( - onFirst: sinks => Drivers.Call( - sinks, + onFirst: sinks => sinks.CallDrivers( LogDriver, KeyInputDriver)); Runner, IUnion2>.Run(component, drivers); diff --git a/Cycle.NET/Drivers.cs b/Cycle.NET/Extensions/DriverExtensions.cs similarity index 89% rename from Cycle.NET/Drivers.cs rename to Cycle.NET/Extensions/DriverExtensions.cs index 205be4c..d708023 100644 --- a/Cycle.NET/Drivers.cs +++ b/Cycle.NET/Extensions/DriverExtensions.cs @@ -4,17 +4,17 @@ namespace Cycle.NET { - public partial class Drivers + public static class DriverExtensions { public static IObservable> - Call< + CallDrivers< TFirstSink, TFirstSource, TSecondSink, TSecondSource>( - IObservable> sinks, Func, IObservable> firstDriver, diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index c9bb4fb..07b668c 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -13,7 +13,7 @@ public Streams() : base() { } public Streams(Dictionary> value) : base(value) { } } - public partial class Drivers : Dictionary, IObservable>> + public class Drivers : Dictionary, IObservable>> { public Drivers() : base() { } } From 35fee2d29b96e46c63aae326ee7f8afaaefc5b67 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 3 Apr 2023 23:22:00 -0400 Subject: [PATCH 18/42] Remove redundant types --- Cycle.NET.Demo/Program.cs | 8 +++----- Cycle.NET/Runner1.cs | 34 ++++++---------------------------- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 1511b06..9b933e3 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -35,13 +35,11 @@ private static IObservable> CycleMain(IObservable, IUnion2>( - CycleMain); - var drivers = new Drivers, IUnion2>( - onFirst: sinks => sinks.CallDrivers( + Runner, IUnion2>.Run( + CycleMain, + sinks => sinks.CallDrivers( LogDriver, KeyInputDriver)); - Runner, IUnion2>.Run(component, drivers); Console.ReadLine(); } } diff --git a/Cycle.NET/Runner1.cs b/Cycle.NET/Runner1.cs index 334b928..78a28b5 100644 --- a/Cycle.NET/Runner1.cs +++ b/Cycle.NET/Runner1.cs @@ -4,42 +4,20 @@ namespace Cycle.NET { - public class Component - { - public Component( - Func, IObservable> main) - { - Main = main; - } - - public Func, IObservable> Main { get; } - } - - public class Drivers - { - public Drivers( - Func, IObservable> onFirst) - { - OnFirst = onFirst; - } - - public Func, IObservable> OnFirst { get; } - } - - public static class Runner + public static class Runner { public static void Run( - Component component, - Drivers drivers) + Func, IObservable> main, + Func, IObservable> drivers) { // Create fake sinks to use to call the drivers to get around the interdependency between // main and the drivers. - var fakeSinks = new ReplaySubject(); + var fakeSinks = new ReplaySubject(); // Call the drivers and collate the returned sources. - var sources = drivers.OnFirst(fakeSinks); + var sources = drivers(fakeSinks); - IObservable sinks = component.Main(sources); + var sinks = main(sources); // Update the sinks returned from main with the sinks used by the drivers. sinks.Subscribe(fakeSinks.OnNext); From 996d75e5471aa2460e4428cb99737b5cb9612972 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Mon, 3 Apr 2023 23:28:25 -0400 Subject: [PATCH 19/42] Collapse down to one runner --- Cycle.NET.Demo/Program.cs | 2 +- ...riverExtensions.cs => RunnerExtensions.cs} | 2 +- Cycle.NET/Runner.cs | 29 ++---- Cycle.NET/Runner1.cs | 26 ----- Cycle.NET/Runner3.cs | 97 ------------------- 5 files changed, 9 insertions(+), 147 deletions(-) rename Cycle.NET/Extensions/{DriverExtensions.cs => RunnerExtensions.cs} (95%) delete mode 100644 Cycle.NET/Runner1.cs delete mode 100644 Cycle.NET/Runner3.cs diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 9b933e3..4a3e6e5 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -35,7 +35,7 @@ private static IObservable> CycleMain(IObservable, IUnion2>.Run( + Runner.Run, IUnion2>( CycleMain, sinks => sinks.CallDrivers( LogDriver, diff --git a/Cycle.NET/Extensions/DriverExtensions.cs b/Cycle.NET/Extensions/RunnerExtensions.cs similarity index 95% rename from Cycle.NET/Extensions/DriverExtensions.cs rename to Cycle.NET/Extensions/RunnerExtensions.cs index d708023..4047506 100644 --- a/Cycle.NET/Extensions/DriverExtensions.cs +++ b/Cycle.NET/Extensions/RunnerExtensions.cs @@ -4,7 +4,7 @@ namespace Cycle.NET { - public static class DriverExtensions + public static class RunnerExtensions { public static IObservable> - { - public Streams() : base() { } - public Streams(Dictionary> value) : base(value) { } - } - - public class Drivers : Dictionary, IObservable>> - { - public Drivers() : base() { } - } - public static class Runner { - public static void Run(Func main, Drivers drivers) + public static void Run( + Func, IObservable> main, + Func, IObservable> drivers) { // Create fake sinks to use to call the drivers to get around the interdependency between // main and the drivers. - var fakeSinks = drivers.ToDictionary(d => d.Key, d => new ReplaySubject()); + var fakeSinks = new ReplaySubject(); // Call the drivers and collate the returned sources. - var sources = drivers.ToDictionary(d => d.Key, d => d.Value(fakeSinks[d.Key])); + var sources = drivers(fakeSinks); - Streams sinks = main(new Streams(sources)); + var sinks = main(sources); // Update the sinks returned from main with the sinks used by the drivers. - foreach (var sink in sinks) - { - sink.Value.Subscribe(s => fakeSinks[sink.Key].OnNext(s)); - } + sinks.Subscribe(fakeSinks.OnNext); } } } diff --git a/Cycle.NET/Runner1.cs b/Cycle.NET/Runner1.cs deleted file mode 100644 index 78a28b5..0000000 --- a/Cycle.NET/Runner1.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Reactive.Linq; -using System.Reactive.Subjects; - -namespace Cycle.NET -{ - public static class Runner - { - public static void Run( - Func, IObservable> main, - Func, IObservable> drivers) - { - // Create fake sinks to use to call the drivers to get around the interdependency between - // main and the drivers. - var fakeSinks = new ReplaySubject(); - - // Call the drivers and collate the returned sources. - var sources = drivers(fakeSinks); - - var sinks = main(sources); - - // Update the sinks returned from main with the sinks used by the drivers. - sinks.Subscribe(fakeSinks.OnNext); - } - } -} diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs deleted file mode 100644 index 585d973..0000000 --- a/Cycle.NET/Runner3.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Reactive.Linq; -using System.Reactive.Subjects; - -namespace Cycle.NET -{ - public class Streams - { - public Streams( - IObservable first, - IObservable second, - IObservable third) - { - First = first; - Second = second; - Third = third; - } - - public IObservable First { get; } - public IObservable Second { get; } - public IObservable Third { get; } - } - - public class Component - { - public Component( - Func, Streams> main) - { - Main = main; - } - - public Func, Streams> Main { get; } - } - - public class Drivers - { - public Drivers( - Func, IObservable> onFirst, - Func, IObservable> onSecond, - Func, IObservable> onThird) - { - OnFirst = onFirst; - OnSecond = onSecond; - OnThird = onThird; - } - - public Func, IObservable> OnFirst { get; } - public Func, IObservable> OnSecond { get; } - public Func, IObservable> OnThird { get; } - } - - public static class Runner - { - public static void Run( - Component component, - Drivers drivers) - { - // Create fake sinks to use to call the drivers to get around the interdependency between - // main and the drivers. - var fakeSinks = new FakeSinks( - new ReplaySubject(), - new ReplaySubject(), - new ReplaySubject()); - - // Call the drivers and collate the returned sources. - var sources = new Streams( - drivers.OnFirst(fakeSinks.First), - drivers.OnSecond(fakeSinks.Second), - drivers.OnThird(fakeSinks.Third)); - - Streams sinks = component.Main(sources); - - // Update the sinks returned from main with the sinks used by the drivers. - sinks.First.Subscribe(fakeSinks.First.OnNext); - sinks.Second.Subscribe(fakeSinks.Second.OnNext); - sinks.Third.Subscribe(fakeSinks.Third.OnNext); - } - - private class FakeSinks - { - public FakeSinks( - ISubject first, - ISubject second, - ISubject third) - { - First = first; - Second = second; - Third = third; - } - - public ISubject First { get; } - public ISubject Second { get; } - public ISubject Third { get; } - } - - } -} From 1ef53ca494dfef51ce0fa638be551a5279118ef4 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Tue, 4 Apr 2023 21:47:18 -0400 Subject: [PATCH 20/42] Scale arity --- .../Extensions/ObservableUnionExtensions.cs | 616 ++++++++++++++++++ Cycle.NET/Extensions/RunnerExtensions.cs | 446 +++++++++++++ Cycle.NET/ObservableUnion.cs | 360 +++++++++- 3 files changed, 1420 insertions(+), 2 deletions(-) diff --git a/Cycle.NET/Extensions/ObservableUnionExtensions.cs b/Cycle.NET/Extensions/ObservableUnionExtensions.cs index 604159e..36461e1 100644 --- a/Cycle.NET/Extensions/ObservableUnionExtensions.cs +++ b/Cycle.NET/Extensions/ObservableUnionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive; using System.Reactive.Linq; using SdgApps.Common.DotnetSealedUnions; @@ -6,6 +7,40 @@ namespace Cycle.NET.Extensions { public static class ObservableUnionExtensions { + public static + IObservable + Split< + TFirst>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return)); + + return firsts; + } + + public static ( + IObservable Nones, + IObservable Firsts) + Split< + TFirst>( + this IObservable> source) + { + var nones = source.SelectMany(s => s.Join( + mapNone: () => Observable.Return(Unit.Default), + mapFirst: _ => Observable.Empty())); + + var firsts = source.SelectMany(s => s.Join( + mapNone: Observable.Empty, + mapFirst: Observable.Return)); + + return ( + Nones: nones, + Firsts: firsts); + } + public static ( IObservable Firsts, IObservable Seconds) @@ -28,5 +63,586 @@ this IObservable Firsts, + IObservable Seconds, + IObservable Thirds) + Split< + TFirst, + TSecond, + TThird>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths) + Split< + TFirst, + TSecond, + TThird, + TFourth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths, + IObservable Sevenths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return, + mapSeventh: _ => Observable.Empty())); + + var sevenths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths, + Sevenths: sevenths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths, + IObservable Sevenths, + IObservable Eighths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return, + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty())); + + var sevenths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: Observable.Return, + mapEighth: _ => Observable.Empty())); + + var eighths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths, + Sevenths: sevenths, + Eighths: eighths); + } + + public static ( + IObservable Firsts, + IObservable Seconds, + IObservable Thirds, + IObservable Fourths, + IObservable Fifths, + IObservable Sixths, + IObservable Sevenths, + IObservable Eighths, + IObservable Ninths) + Split< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth, + TNinth>( + this IObservable> source) + { + var firsts = source.SelectMany(s => s.Join( + mapFirst: Observable.Return, + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var seconds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: Observable.Return, + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var thirds = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: Observable.Return, + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var fourths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: Observable.Return, + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var fifths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: Observable.Return, + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var sixths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: Observable.Return, + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var sevenths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: Observable.Return, + mapEighth: _ => Observable.Empty(), + mapNinth: _ => Observable.Empty())); + + var eighths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: Observable.Return, + mapNinth: _ => Observable.Empty())); + + var ninths = source.SelectMany(s => s.Join( + mapFirst: _ => Observable.Empty(), + mapSecond: _ => Observable.Empty(), + mapThird: _ => Observable.Empty(), + mapFourth: _ => Observable.Empty(), + mapFifth: _ => Observable.Empty(), + mapSixth: _ => Observable.Empty(), + mapSeventh: _ => Observable.Empty(), + mapEighth: _ => Observable.Empty(), + mapNinth: Observable.Return)); + + return ( + Firsts: firsts, + Seconds: seconds, + Thirds: thirds, + Fourths: fourths, + Fifths: fifths, + Sixths: sixths, + Sevenths: sevenths, + Eighths: eighths, + Ninths: ninths); + } } } diff --git a/Cycle.NET/Extensions/RunnerExtensions.cs b/Cycle.NET/Extensions/RunnerExtensions.cs index 4047506..f96a4c2 100644 --- a/Cycle.NET/Extensions/RunnerExtensions.cs +++ b/Cycle.NET/Extensions/RunnerExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive; using Cycle.NET.Extensions; using SdgApps.Common.DotnetSealedUnions; @@ -6,6 +7,45 @@ namespace Cycle.NET { public static class RunnerExtensions { + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource>( + this IObservable> sinks, + Func, IObservable> firstDriver) + { + var firstSink = sinks.Split(); + + var firstSource = firstDriver(firstSink); + + return ObservableUnion.Merge( + firstSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource>( + this IObservable> sinks, + Func, IObservable> noneDriver, + Func, IObservable> firstDriver) + { + var ( + noneSink, + firstSink) = sinks.Split(); + + var noneSource = noneDriver(noneSink); + var firstSource = firstDriver(firstSink); + + return ObservableUnion.Merge( + noneSource, + firstSource); + } + public static IObservable> @@ -31,5 +71,411 @@ this IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver) + { + var ( + firstSink, + secondSink, + thirdSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource, + TSeventhSink, + TSeventhSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink, + seventhSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + var seventhSource = seventhDriver(seventhSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource, + seventhSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource, + TSeventhSink, + TSeventhSource, + TEighthSink, + TEighthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink, + seventhSink, + eighthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + var seventhSource = seventhDriver(seventhSink); + var eighthSource = eighthDriver(eighthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource, + seventhSource, + eighthSource); + } + + public static IObservable> + CallDrivers< + TFirstSink, + TFirstSource, + TSecondSink, + TSecondSource, + TThirdSink, + TThirdSource, + TFourthSink, + TFourthSource, + TFifthSink, + TFifthSource, + TSixthSink, + TSixthSource, + TSeventhSink, + TSeventhSource, + TEighthSink, + TEighthSource, + TNinthSink, + TNinthSource>( + this IObservable> sinks, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver, + Func, IObservable> ninthDriver) + { + var ( + firstSink, + secondSink, + thirdSink, + fourthSink, + fifthSink, + sixthSink, + seventhSink, + eighthSink, + ninthSink) = sinks.Split(); + + var firstSource = firstDriver(firstSink); + var secondSource = secondDriver(secondSink); + var thirdSource = thirdDriver(thirdSink); + var fourthSource = fourthDriver(fourthSink); + var fifthSource = fifthDriver(fifthSink); + var sixthSource = sixthDriver(sixthSink); + var seventhSource = seventhDriver(seventhSink); + var eighthSource = eighthDriver(eighthSink); + var ninthSource = ninthDriver(ninthSink); + + return ObservableUnion.Merge( + firstSource, + secondSource, + thirdSource, + fourthSource, + fifthSource, + sixthSource, + seventhSource, + eighthSource, + ninthSource); + } } } diff --git a/Cycle.NET/ObservableUnion.cs b/Cycle.NET/ObservableUnion.cs index e6a3060..f2c9afd 100644 --- a/Cycle.NET/ObservableUnion.cs +++ b/Cycle.NET/ObservableUnion.cs @@ -1,4 +1,5 @@ using System; +using System.Reactive; using System.Reactive.Linq; using SdgApps.Common.DotnetSealedUnions; using SdgApps.Common.DotnetSealedUnions.Generic; @@ -7,11 +8,51 @@ namespace Cycle.NET { public static class ObservableUnion { - public static IObservable> Merge( + public static IObservable> + Merge< + TFirst>( + IObservable firsts) + { + var fac = GenericUnions.NulletFactory< + TFirst>(); + + var unionFirsts = firsts.Select(fac.First); + + return Observable.Merge( + unionFirsts); + } + + public static IObservable> + Merge< + TFirst>( + IObservable nones, + IObservable firsts) + { + var fac = GenericUnions.SingletFactory< + TFirst>(); + + var unionNones = nones.Select(_ => fac.None()); + var unionFirsts = firsts.Select(fac.First); + + return Observable.Merge( + unionNones, + unionFirsts); + } + + public static IObservable> + Merge< + TFirst, + TSecond>( IObservable firsts, IObservable seconds) { - var fac = GenericUnions.DoubletFactory(); + var fac = GenericUnions.DoubletFactory< + TFirst, + TSecond>(); var unionFirsts = firsts.Select(fac.First); var unionSeconds = seconds.Select(fac.Second); @@ -20,5 +61,320 @@ public static IObservable> Merge( unionFirsts, unionSeconds); } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird>( + IObservable firsts, + IObservable seconds, + IObservable thirds) + { + var fac = GenericUnions.TripletFactory< + TFirst, + TSecond, + TThird>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths) + { + var fac = GenericUnions.QuartetFactory< + TFirst, + TSecond, + TThird, + TFourth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths) + { + var fac = GenericUnions.QuintetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths) + { + var fac = GenericUnions.SextetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths, + IObservable sevenths) + { + var fac = GenericUnions.SeptetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + var unionSevenths = sevenths.Select(fac.Seventh); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths, + unionSevenths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths, + IObservable sevenths, + IObservable eighths) + { + var fac = GenericUnions.OctetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + var unionSevenths = sevenths.Select(fac.Seventh); + var unionEighths = eighths.Select(fac.Eighth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths, + unionSevenths, + unionEighths); + } + + public static IObservable> + Merge< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth, + TNinth>( + IObservable firsts, + IObservable seconds, + IObservable thirds, + IObservable fourths, + IObservable fifths, + IObservable sixths, + IObservable sevenths, + IObservable eighths, + IObservable ninths) + { + var fac = GenericUnions.NonetFactory< + TFirst, + TSecond, + TThird, + TFourth, + TFifth, + TSixth, + TSeventh, + TEighth, + TNinth>(); + + var unionFirsts = firsts.Select(fac.First); + var unionSeconds = seconds.Select(fac.Second); + var unionThirds = thirds.Select(fac.Third); + var unionFourths = fourths.Select(fac.Fourth); + var unionFifths = fifths.Select(fac.Fifth); + var unionSixths = sixths.Select(fac.Sixth); + var unionSevenths = sevenths.Select(fac.Seventh); + var unionEighths = eighths.Select(fac.Eighth); + var unionNinths = ninths.Select(fac.Ninth); + + return Observable.Merge( + unionFirsts, + unionSeconds, + unionThirds, + unionFourths, + unionFifths, + unionSixths, + unionSevenths, + unionEighths, + unionNinths); + } } } From f93ea4152e941126aa4f27f04655b61022ead489 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Tue, 4 Apr 2023 22:12:32 -0400 Subject: [PATCH 21/42] Improve typing --- Cycle.NET.Demo/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 4a3e6e5..4f9b3ea 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -35,9 +35,9 @@ private static IObservable> CycleMain(IObservable, IUnion2>( + Runner.Run( CycleMain, - sinks => sinks.CallDrivers( + (IObservable> sinks) => sinks.CallDrivers( LogDriver, KeyInputDriver)); Console.ReadLine(); From c84094b80a996def5ab3fe2890bcb7ed9d9c19e2 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Thu, 20 Apr 2023 20:53:39 -0400 Subject: [PATCH 22/42] Revert "Collapse down to one runner" This reverts commit 996d75e5471aa2460e4428cb99737b5cb9612972. --- Cycle.NET.Demo/Program.cs | 2 +- Cycle.NET/Runner.cs | 29 +++++++++--- Cycle.NET/Runner1.cs | 26 +++++++++++ Cycle.NET/Runner3.cs | 97 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 Cycle.NET/Runner1.cs create mode 100644 Cycle.NET/Runner3.cs diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 4f9b3ea..bd4c984 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -35,7 +35,7 @@ private static IObservable> CycleMain(IObservable, IUnion2>.Run( CycleMain, (IObservable> sinks) => sinks.CallDrivers( LogDriver, diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index 2d6b1e9..07b668c 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -1,26 +1,41 @@ using System; +using System.Collections.Generic; +using System.Reactive; using System.Reactive.Linq; using System.Reactive.Subjects; +using System.Linq; namespace Cycle.NET { + public class Streams : Dictionary> + { + public Streams() : base() { } + public Streams(Dictionary> value) : base(value) { } + } + + public class Drivers : Dictionary, IObservable>> + { + public Drivers() : base() { } + } + public static class Runner { - public static void Run( - Func, IObservable> main, - Func, IObservable> drivers) + public static void Run(Func main, Drivers drivers) { // Create fake sinks to use to call the drivers to get around the interdependency between // main and the drivers. - var fakeSinks = new ReplaySubject(); + var fakeSinks = drivers.ToDictionary(d => d.Key, d => new ReplaySubject()); // Call the drivers and collate the returned sources. - var sources = drivers(fakeSinks); + var sources = drivers.ToDictionary(d => d.Key, d => d.Value(fakeSinks[d.Key])); - var sinks = main(sources); + Streams sinks = main(new Streams(sources)); // Update the sinks returned from main with the sinks used by the drivers. - sinks.Subscribe(fakeSinks.OnNext); + foreach (var sink in sinks) + { + sink.Value.Subscribe(s => fakeSinks[sink.Key].OnNext(s)); + } } } } diff --git a/Cycle.NET/Runner1.cs b/Cycle.NET/Runner1.cs new file mode 100644 index 0000000..78a28b5 --- /dev/null +++ b/Cycle.NET/Runner1.cs @@ -0,0 +1,26 @@ +using System; +using System.Reactive.Linq; +using System.Reactive.Subjects; + +namespace Cycle.NET +{ + public static class Runner + { + public static void Run( + Func, IObservable> main, + Func, IObservable> drivers) + { + // Create fake sinks to use to call the drivers to get around the interdependency between + // main and the drivers. + var fakeSinks = new ReplaySubject(); + + // Call the drivers and collate the returned sources. + var sources = drivers(fakeSinks); + + var sinks = main(sources); + + // Update the sinks returned from main with the sinks used by the drivers. + sinks.Subscribe(fakeSinks.OnNext); + } + } +} diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs new file mode 100644 index 0000000..585d973 --- /dev/null +++ b/Cycle.NET/Runner3.cs @@ -0,0 +1,97 @@ +using System; +using System.Reactive.Linq; +using System.Reactive.Subjects; + +namespace Cycle.NET +{ + public class Streams + { + public Streams( + IObservable first, + IObservable second, + IObservable third) + { + First = first; + Second = second; + Third = third; + } + + public IObservable First { get; } + public IObservable Second { get; } + public IObservable Third { get; } + } + + public class Component + { + public Component( + Func, Streams> main) + { + Main = main; + } + + public Func, Streams> Main { get; } + } + + public class Drivers + { + public Drivers( + Func, IObservable> onFirst, + Func, IObservable> onSecond, + Func, IObservable> onThird) + { + OnFirst = onFirst; + OnSecond = onSecond; + OnThird = onThird; + } + + public Func, IObservable> OnFirst { get; } + public Func, IObservable> OnSecond { get; } + public Func, IObservable> OnThird { get; } + } + + public static class Runner + { + public static void Run( + Component component, + Drivers drivers) + { + // Create fake sinks to use to call the drivers to get around the interdependency between + // main and the drivers. + var fakeSinks = new FakeSinks( + new ReplaySubject(), + new ReplaySubject(), + new ReplaySubject()); + + // Call the drivers and collate the returned sources. + var sources = new Streams( + drivers.OnFirst(fakeSinks.First), + drivers.OnSecond(fakeSinks.Second), + drivers.OnThird(fakeSinks.Third)); + + Streams sinks = component.Main(sources); + + // Update the sinks returned from main with the sinks used by the drivers. + sinks.First.Subscribe(fakeSinks.First.OnNext); + sinks.Second.Subscribe(fakeSinks.Second.OnNext); + sinks.Third.Subscribe(fakeSinks.Third.OnNext); + } + + private class FakeSinks + { + public FakeSinks( + ISubject first, + ISubject second, + ISubject third) + { + First = first; + Second = second; + Third = third; + } + + public ISubject First { get; } + public ISubject Second { get; } + public ISubject Third { get; } + } + + } +} From 88125fa5b51c68581d23637e3ab9222a45d541b4 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Thu, 20 Apr 2023 20:39:21 -0400 Subject: [PATCH 23/42] Rename kernel --- Cycle.NET.Demo/Program.cs | 2 +- Cycle.NET/{Runner1.cs => Kernel.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename Cycle.NET/{Runner1.cs => Kernel.cs} (94%) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index bd4c984..fd0f106 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -35,7 +35,7 @@ private static IObservable> CycleMain(IObservable, IUnion2>.Run( + Kernel, IUnion2>.Run( CycleMain, (IObservable> sinks) => sinks.CallDrivers( LogDriver, diff --git a/Cycle.NET/Runner1.cs b/Cycle.NET/Kernel.cs similarity index 94% rename from Cycle.NET/Runner1.cs rename to Cycle.NET/Kernel.cs index 78a28b5..eebab10 100644 --- a/Cycle.NET/Runner1.cs +++ b/Cycle.NET/Kernel.cs @@ -4,7 +4,7 @@ namespace Cycle.NET { - public static class Runner + public static class Kernel { public static void Run( Func, IObservable> main, From 8a58d91537036d798dc8ea1249a05fcc0360e0ed Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Thu, 20 Apr 2023 20:58:49 -0400 Subject: [PATCH 24/42] Improve typing again --- Cycle.NET.Demo/Program.cs | 2 +- Cycle.NET/Kernel.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index fd0f106..3865c70 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -35,7 +35,7 @@ private static IObservable> CycleMain(IObservable, IUnion2>.Run( + Kernel.Run( CycleMain, (IObservable> sinks) => sinks.CallDrivers( LogDriver, diff --git a/Cycle.NET/Kernel.cs b/Cycle.NET/Kernel.cs index eebab10..b836d3d 100644 --- a/Cycle.NET/Kernel.cs +++ b/Cycle.NET/Kernel.cs @@ -4,9 +4,9 @@ namespace Cycle.NET { - public static class Kernel + public static class Kernel { - public static void Run( + public static void Run( Func, IObservable> main, Func, IObservable> drivers) { From 3ddb3d08d262c400277ce651cbc931f75faa530d Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Fri, 21 Apr 2023 16:00:04 -0400 Subject: [PATCH 25/42] Add object split/merge --- Cycle.NET/Extensions/ObservableUnionExtensions.cs | 14 ++++++++++++++ Cycle.NET/ObservableUnion.cs | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/Cycle.NET/Extensions/ObservableUnionExtensions.cs b/Cycle.NET/Extensions/ObservableUnionExtensions.cs index 36461e1..e0c9e00 100644 --- a/Cycle.NET/Extensions/ObservableUnionExtensions.cs +++ b/Cycle.NET/Extensions/ObservableUnionExtensions.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reactive; using System.Reactive.Linq; using SdgApps.Common.DotnetSealedUnions; @@ -7,6 +9,18 @@ namespace Cycle.NET.Extensions { public static class ObservableUnionExtensions { + public static + IDictionary> + Split( + this IObservable> source, + IEnumerable keys) => + keys + .Select(k => new KeyValuePair>(k, source + .Where(p => p.Key == k) + .Select(p => Observable + .Return(p.Value)))) + .ToDictionary(p => p.Key, p => p.Value); + public static IObservable Split< diff --git a/Cycle.NET/ObservableUnion.cs b/Cycle.NET/ObservableUnion.cs index f2c9afd..a74198d 100644 --- a/Cycle.NET/ObservableUnion.cs +++ b/Cycle.NET/ObservableUnion.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reactive; using System.Reactive.Linq; using SdgApps.Common.DotnetSealedUnions; @@ -8,6 +10,14 @@ namespace Cycle.NET { public static class ObservableUnion { + public static IObservable> + Merge( + IDictionary> streams) => + Observable + .Merge(streams + .Select(p => p.Value + .Select(v => new KeyValuePair(p.Key, v)))); + public static IObservable> Merge< From a3093204b00d2d158410c19d814db33887261f0f Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Fri, 21 Apr 2023 16:27:54 -0400 Subject: [PATCH 26/42] Implement runner with kernel --- Cycle.NET/Extensions/RunnerExtensions.cs | 16 ++++++++++++ Cycle.NET/Runner.cs | 32 +++++++++++++++--------- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Cycle.NET/Extensions/RunnerExtensions.cs b/Cycle.NET/Extensions/RunnerExtensions.cs index f96a4c2..385aabe 100644 --- a/Cycle.NET/Extensions/RunnerExtensions.cs +++ b/Cycle.NET/Extensions/RunnerExtensions.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Reactive; using Cycle.NET.Extensions; using SdgApps.Common.DotnetSealedUnions; @@ -7,6 +9,20 @@ namespace Cycle.NET { public static class RunnerExtensions { + public static IObservable> + CallDrivers( + this IObservable> sinks, + IDictionary, IObservable>> drivers) + { + var splitSinks = sinks.Split(drivers.Keys); + + var splitSources = splitSinks + .ToDictionary(p => p.Key, p => drivers[p.Key](p.Value)); + + return ObservableUnion.Merge( + splitSources); + } + public static IObservable> CallDrivers< diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index 07b668c..fbfd765 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -4,6 +4,7 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using System.Linq; +using Cycle.NET.Extensions; namespace Cycle.NET { @@ -20,22 +21,29 @@ public Drivers() : base() { } public static class Runner { - public static void Run(Func main, Drivers drivers) + private static IObservable> CycleMain( + IObservable> sources, + IEnumerable keys, + Func main) { - // Create fake sinks to use to call the drivers to get around the interdependency between - // main and the drivers. - var fakeSinks = drivers.ToDictionary(d => d.Key, d => new ReplaySubject()); + var sourceStreams = new Streams(sources + .Split(keys) + .ToDictionary(p => p.Key, p => p.Value)); - // Call the drivers and collate the returned sources. - var sources = drivers.ToDictionary(d => d.Key, d => d.Value(fakeSinks[d.Key])); + var sinkStreams = main(sourceStreams); - Streams sinks = main(new Streams(sources)); + return ObservableUnion + .Merge(sinkStreams); + } - // Update the sinks returned from main with the sinks used by the drivers. - foreach (var sink in sinks) - { - sink.Value.Subscribe(s => fakeSinks[sink.Key].OnNext(s)); - } + public static void Run(Func main, Drivers drivers) + { + Kernel.Run( + sources => CycleMain( + sources, + drivers.Keys, + main), + (IObservable> sinks) => sinks.CallDrivers(drivers)); } } } From 55c176db088e0c37562039a8caf46bfb7955c9ec Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Fri, 21 Apr 2023 21:20:31 -0400 Subject: [PATCH 27/42] Test old and new demos alongside --- Cycle.NET.Demo/Program.cs | 34 +++++++++++++++++-- .../Extensions/ObservableUnionExtensions.cs | 3 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 3865c70..5bca168 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,14 +1,20 @@ using System; -using System.Reactive; +using System.Collections.Generic; using System.Reactive.Linq; using Cycle.NET.Extensions; using SdgApps.Common.DotnetSealedUnions; -using SdgApps.Common.DotnetSealedUnions.Generic; namespace Cycle.NET.Demo { class Program { + private static IObservable LogDriver(IObservable sinks) + { + sinks.Cast().Subscribe(i => Console.WriteLine("Log " + i.ToString())); + + return Observable.Empty(); + } + private static IObservable LogDriver(IObservable sinks) { sinks.Subscribe(i => Console.WriteLine("Log " + i.ToString())); @@ -16,11 +22,26 @@ private static IObservable LogDriver(IObservable sinks) return Observable.Empty(); } + private static IObservable KeyInputDriver(IObservable sinks) + { + return Observable.Range(20, 5).Select(i => (object)i); + } + private static IObservable KeyInputDriver(IObservable sinks) { return Observable.Range(20, 5); } + private static Streams CycleMain(Streams sources) + { + var sinks = new Streams + { + { "log", sources["keys"] } + }; + + return sinks; + } + private static IObservable> CycleMain(IObservable> sources) { var ( @@ -33,13 +54,22 @@ private static IObservable> CycleMain(IObservable()); } + static void Main(string[] args) { + var drivers = new Drivers + { + { "log", LogDriver }, + { "keys", KeyInputDriver } + }; + Runner.Run(CycleMain, drivers); + Kernel.Run( CycleMain, (IObservable> sinks) => sinks.CallDrivers( LogDriver, KeyInputDriver)); + Console.ReadLine(); } } diff --git a/Cycle.NET/Extensions/ObservableUnionExtensions.cs b/Cycle.NET/Extensions/ObservableUnionExtensions.cs index e0c9e00..44f4c84 100644 --- a/Cycle.NET/Extensions/ObservableUnionExtensions.cs +++ b/Cycle.NET/Extensions/ObservableUnionExtensions.cs @@ -17,8 +17,7 @@ public static keys .Select(k => new KeyValuePair>(k, source .Where(p => p.Key == k) - .Select(p => Observable - .Return(p.Value)))) + .Select(p => p.Value))) .ToDictionary(p => p.Key, p => p.Value); public static From d9872f1b0f24a628d8fbec4f43cb701fc3ccdf47 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Fri, 21 Apr 2023 21:28:26 -0400 Subject: [PATCH 28/42] Create runner2 from 3 --- Cycle.NET/Runner2.cs | 85 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Cycle.NET/Runner2.cs diff --git a/Cycle.NET/Runner2.cs b/Cycle.NET/Runner2.cs new file mode 100644 index 0000000..8ac5411 --- /dev/null +++ b/Cycle.NET/Runner2.cs @@ -0,0 +1,85 @@ +using System; +using System.Reactive.Linq; +using System.Reactive.Subjects; + +namespace Cycle.NET +{ + public class Streams + { + public Streams( + IObservable first, + IObservable second) + { + First = first; + Second = second; + } + + public IObservable First { get; } + public IObservable Second { get; } + } + + public class Component + { + public Component( + Func, Streams> main) + { + Main = main; + } + + public Func, Streams> Main { get; } + } + + public class Drivers + { + public Drivers( + Func, IObservable> onFirst, + Func, IObservable> onSecond) + { + OnFirst = onFirst; + OnSecond = onSecond; + } + + public Func, IObservable> OnFirst { get; } + public Func, IObservable> OnSecond { get; } + } + + public static class Runner + { + public static void Run( + Component component, + Drivers drivers) + { + // Create fake sinks to use to call the drivers to get around the interdependency between + // main and the drivers. + var fakeSinks = new FakeSinks( + new ReplaySubject(), + new ReplaySubject()); + + // Call the drivers and collate the returned sources. + var sources = new Streams( + drivers.OnFirst(fakeSinks.First), + drivers.OnSecond(fakeSinks.Second)); + + Streams sinks = component.Main(sources); + + // Update the sinks returned from main with the sinks used by the drivers. + sinks.First.Subscribe(fakeSinks.First.OnNext); + sinks.Second.Subscribe(fakeSinks.Second.OnNext); + } + + private class FakeSinks + { + public FakeSinks( + ISubject first, + ISubject second) + { + First = first; + Second = second; + } + + public ISubject First { get; } + public ISubject Second { get; } + } + + } +} From 146c78a806ebe5f347b9dc4a8253decdf6e455a0 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Fri, 21 Apr 2023 21:46:20 -0400 Subject: [PATCH 29/42] Implement runner2 with kernel --- Cycle.NET/Runner2.cs | 55 ++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/Cycle.NET/Runner2.cs b/Cycle.NET/Runner2.cs index 8ac5411..2f517f2 100644 --- a/Cycle.NET/Runner2.cs +++ b/Cycle.NET/Runner2.cs @@ -1,6 +1,8 @@ using System; using System.Reactive.Linq; using System.Reactive.Subjects; +using Cycle.NET.Extensions; +using SdgApps.Common.DotnetSealedUnions; namespace Cycle.NET { @@ -43,28 +45,47 @@ public Drivers( public Func, IObservable> OnSecond { get; } } - public static class Runner + public static class Runner< + TSource1, + TSink1, + TSource2, + TSink2> { - public static void Run( - Component component, - Drivers drivers) + private static IObservable> + CycleMain( + IObservable> sources, + Func< + Streams< + TSource1, + TSource2>, + Streams< + TSink1, + TSink2>> main) { - // Create fake sinks to use to call the drivers to get around the interdependency between - // main and the drivers. - var fakeSinks = new FakeSinks( - new ReplaySubject(), - new ReplaySubject()); + var sourceStreams = sources.Split(); - // Call the drivers and collate the returned sources. - var sources = new Streams( - drivers.OnFirst(fakeSinks.First), - drivers.OnSecond(fakeSinks.Second)); + var sinkStreams = main(new Streams( + sourceStreams.Firsts, + sourceStreams.Seconds)); - Streams sinks = component.Main(sources); + return ObservableUnion.Merge( + sinkStreams.First, + sinkStreams.Second); + } - // Update the sinks returned from main with the sinks used by the drivers. - sinks.First.Subscribe(fakeSinks.First.OnNext); - sinks.Second.Subscribe(fakeSinks.Second.OnNext); + public static void Run( + Component component, + Drivers drivers) + { + Kernel.Run( + sources => CycleMain(sources, component.Main), + (IObservable> sinks) => sinks.CallDrivers( + drivers.OnFirst, + drivers.OnSecond)); } private class FakeSinks From 639b318142a6f85f486fcf9506565e8bfa1effc4 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Fri, 21 Apr 2023 21:57:21 -0400 Subject: [PATCH 30/42] Remove redundant types --- Cycle.NET/Runner2.cs | 89 ++++++++++---------------------------------- 1 file changed, 20 insertions(+), 69 deletions(-) diff --git a/Cycle.NET/Runner2.cs b/Cycle.NET/Runner2.cs index 2f517f2..487732b 100644 --- a/Cycle.NET/Runner2.cs +++ b/Cycle.NET/Runner2.cs @@ -6,45 +6,6 @@ namespace Cycle.NET { - public class Streams - { - public Streams( - IObservable first, - IObservable second) - { - First = first; - Second = second; - } - - public IObservable First { get; } - public IObservable Second { get; } - } - - public class Component - { - public Component( - Func, Streams> main) - { - Main = main; - } - - public Func, Streams> Main { get; } - } - - public class Drivers - { - public Drivers( - Func, IObservable> onFirst, - Func, IObservable> onSecond) - { - OnFirst = onFirst; - OnSecond = onSecond; - } - - public Func, IObservable> OnFirst { get; } - public Func, IObservable> OnSecond { get; } - } - public static class Runner< TSource1, TSink1, @@ -59,48 +20,38 @@ private static IObservable> sources, Func< - Streams< - TSource1, - TSource2>, - Streams< - TSink1, - TSink2>> main) + IObservable, + IObservable, + ( + IObservable, + IObservable)> main) { var sourceStreams = sources.Split(); - var sinkStreams = main(new Streams( + var sinkStreams = main( sourceStreams.Firsts, - sourceStreams.Seconds)); + sourceStreams.Seconds); return ObservableUnion.Merge( - sinkStreams.First, - sinkStreams.Second); + sinkStreams.Item1, + sinkStreams.Item2); } public static void Run( - Component component, - Drivers drivers) + Func< + IObservable, + IObservable, + ( + IObservable, + IObservable)> main, + Func, IObservable> onFirst, + Func, IObservable> onSecond) { Kernel.Run( - sources => CycleMain(sources, component.Main), + sources => CycleMain(sources, main), (IObservable> sinks) => sinks.CallDrivers( - drivers.OnFirst, - drivers.OnSecond)); + onFirst, + onSecond)); } - - private class FakeSinks - { - public FakeSinks( - ISubject first, - ISubject second) - { - First = first; - Second = second; - } - - public ISubject First { get; } - public ISubject Second { get; } - } - } } From 8430fdec1484037d7824d5a9f57947ebdabf0f60 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Fri, 21 Apr 2023 22:01:22 -0400 Subject: [PATCH 31/42] Use runner2 --- Cycle.NET.Demo/Program.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 5bca168..4c418f2 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -42,15 +42,16 @@ private static Streams CycleMain(Streams sources) return sinks; } - private static IObservable> CycleMain(IObservable> sources) + private static ( + IObservable LogSinks, + IObservable KeyInputSinks) + CycleMain( + IObservable logSources, + IObservable keyInputSources) { - var ( - _, - keyInputSources) = sources.Split(); - var logSink = keyInputSources; - return ObservableUnion.Merge( + return ( logSink, Observable.Empty()); } @@ -64,11 +65,10 @@ static void Main(string[] args) }; Runner.Run(CycleMain, drivers); - Kernel.Run( + Runner.Run( CycleMain, - (IObservable> sinks) => sinks.CallDrivers( - LogDriver, - KeyInputDriver)); + LogDriver, + KeyInputDriver); Console.ReadLine(); } From 79eeacca332906bfa9deaa6818b8b3961a5a52e3 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Sat, 22 Apr 2023 14:06:22 -0400 Subject: [PATCH 32/42] Clean up runner2 --- Cycle.NET/Runner2.cs | 60 ++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/Cycle.NET/Runner2.cs b/Cycle.NET/Runner2.cs index 487732b..dd28f9a 100644 --- a/Cycle.NET/Runner2.cs +++ b/Cycle.NET/Runner2.cs @@ -12,46 +12,36 @@ public static class Runner< TSource2, TSink2> { - private static IObservable> - CycleMain( - IObservable> sources, - Func< - IObservable, - IObservable, - ( - IObservable, - IObservable)> main) - { - var sourceStreams = sources.Split(); - - var sinkStreams = main( - sourceStreams.Firsts, - sourceStreams.Seconds); - - return ObservableUnion.Merge( - sinkStreams.Item1, - sinkStreams.Item2); - } - public static void Run( Func< IObservable, IObservable, ( - IObservable, - IObservable)> main, - Func, IObservable> onFirst, - Func, IObservable> onSecond) - { + IObservable FirstSinks, + IObservable SecondSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver) => Kernel.Run( - sources => CycleMain(sources, main), - (IObservable> sinks) => sinks.CallDrivers( - onFirst, - onSecond)); - } + sources => + { + var ( + firstSources, + secondSources) = sources.Split(); + + var ( + firstSinks, + secondSinks) = main( + firstSources, + secondSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver)); } } From 4dfa7ae617005a26a96f02851b562d9381fdc15c Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Sat, 22 Apr 2023 14:11:26 -0400 Subject: [PATCH 33/42] Merge runner2 into runner --- Cycle.NET.Demo/Program.cs | 8 +++++-- Cycle.NET/Runner.cs | 37 ++++++++++++++++++++++++++++++ Cycle.NET/Runner2.cs | 47 --------------------------------------- 3 files changed, 43 insertions(+), 49 deletions(-) delete mode 100644 Cycle.NET/Runner2.cs diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 4c418f2..9f2fc01 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -65,8 +65,12 @@ static void Main(string[] args) }; Runner.Run(CycleMain, drivers); - Runner.Run( - CycleMain, + Runner.Run( + ( + IObservable logSources, + IObservable keyInputSources) => CycleMain( + logSources, + keyInputSources), LogDriver, KeyInputDriver); diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index fbfd765..86779c9 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -5,6 +5,7 @@ using System.Reactive.Subjects; using System.Linq; using Cycle.NET.Extensions; +using SdgApps.Common.DotnetSealedUnions; namespace Cycle.NET { @@ -45,5 +46,41 @@ public static void Run(Func main, Drivers drivers) main), (IObservable> sinks) => sinks.CallDrivers(drivers)); } + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2>( + Func< + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources) = sources.Split(); + + var ( + firstSinks, + secondSinks) = main( + firstSources, + secondSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver)); } } diff --git a/Cycle.NET/Runner2.cs b/Cycle.NET/Runner2.cs deleted file mode 100644 index dd28f9a..0000000 --- a/Cycle.NET/Runner2.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using Cycle.NET.Extensions; -using SdgApps.Common.DotnetSealedUnions; - -namespace Cycle.NET -{ - public static class Runner< - TSource1, - TSink1, - TSource2, - TSink2> - { - public static void Run( - Func< - IObservable, - IObservable, - ( - IObservable FirstSinks, - IObservable SecondSinks)> main, - Func, IObservable> firstDriver, - Func, IObservable> secondDriver) => - Kernel.Run( - sources => - { - var ( - firstSources, - secondSources) = sources.Split(); - - var ( - firstSinks, - secondSinks) = main( - firstSources, - secondSources); - - return ObservableUnion.Merge( - firstSinks, - secondSinks); - }, - (IObservable> sinks) => sinks.CallDrivers( - firstDriver, - secondDriver)); - } -} From a155f25ee692cf7c28a8a4905fdb32b6ae3dabb0 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Sat, 22 Apr 2023 19:58:16 -0400 Subject: [PATCH 34/42] Merge in runner3 --- Cycle.NET/Runner.cs | 51 +++++++++++++++++++++-- Cycle.NET/Runner3.cs | 97 -------------------------------------------- 2 files changed, 48 insertions(+), 100 deletions(-) delete mode 100644 Cycle.NET/Runner3.cs diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index 86779c9..f61ae55 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -37,15 +37,13 @@ private static IObservable> CycleMain( .Merge(sinkStreams); } - public static void Run(Func main, Drivers drivers) - { + public static void Run(Func main, Drivers drivers) => Kernel.Run( sources => CycleMain( sources, drivers.Keys, main), (IObservable> sinks) => sinks.CallDrivers(drivers)); - } public static void Run< TSource1, @@ -82,5 +80,52 @@ public static void Run< TSink2>> sinks) => sinks.CallDrivers( firstDriver, secondDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3>( + Func< + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks) = main( + firstSources, + secondSources, + thirdSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver)); } } diff --git a/Cycle.NET/Runner3.cs b/Cycle.NET/Runner3.cs deleted file mode 100644 index 585d973..0000000 --- a/Cycle.NET/Runner3.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Reactive.Linq; -using System.Reactive.Subjects; - -namespace Cycle.NET -{ - public class Streams - { - public Streams( - IObservable first, - IObservable second, - IObservable third) - { - First = first; - Second = second; - Third = third; - } - - public IObservable First { get; } - public IObservable Second { get; } - public IObservable Third { get; } - } - - public class Component - { - public Component( - Func, Streams> main) - { - Main = main; - } - - public Func, Streams> Main { get; } - } - - public class Drivers - { - public Drivers( - Func, IObservable> onFirst, - Func, IObservable> onSecond, - Func, IObservable> onThird) - { - OnFirst = onFirst; - OnSecond = onSecond; - OnThird = onThird; - } - - public Func, IObservable> OnFirst { get; } - public Func, IObservable> OnSecond { get; } - public Func, IObservable> OnThird { get; } - } - - public static class Runner - { - public static void Run( - Component component, - Drivers drivers) - { - // Create fake sinks to use to call the drivers to get around the interdependency between - // main and the drivers. - var fakeSinks = new FakeSinks( - new ReplaySubject(), - new ReplaySubject(), - new ReplaySubject()); - - // Call the drivers and collate the returned sources. - var sources = new Streams( - drivers.OnFirst(fakeSinks.First), - drivers.OnSecond(fakeSinks.Second), - drivers.OnThird(fakeSinks.Third)); - - Streams sinks = component.Main(sources); - - // Update the sinks returned from main with the sinks used by the drivers. - sinks.First.Subscribe(fakeSinks.First.OnNext); - sinks.Second.Subscribe(fakeSinks.Second.OnNext); - sinks.Third.Subscribe(fakeSinks.Third.OnNext); - } - - private class FakeSinks - { - public FakeSinks( - ISubject first, - ISubject second, - ISubject third) - { - First = first; - Second = second; - Third = third; - } - - public ISubject First { get; } - public ISubject Second { get; } - public ISubject Third { get; } - } - - } -} From 6ca585fb4b13427964034343d261417bd4b271db Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Sat, 22 Apr 2023 21:37:13 -0400 Subject: [PATCH 35/42] Scale run arity --- Cycle.NET/Runner.cs | 596 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 578 insertions(+), 18 deletions(-) diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index f61ae55..aea62fe 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -22,28 +22,75 @@ public Drivers() : base() { } public static class Runner { - private static IObservable> CycleMain( - IObservable> sources, - IEnumerable keys, - Func main) - { - var sourceStreams = new Streams(sources - .Split(keys) - .ToDictionary(p => p.Key, p => p.Value)); + public static void Run(Func main, Drivers drivers) => + Kernel.Run( + sources => + { + var sourceStreams = new Streams(sources + .Split(drivers.Keys) + .ToDictionary(p => p.Key, p => p.Value)); - var sinkStreams = main(sourceStreams); + var sinkStreams = main(sourceStreams); - return ObservableUnion - .Merge(sinkStreams); - } + return ObservableUnion + .Merge(sinkStreams); + }, + (IObservable> sinks) => sinks.CallDrivers(drivers)); - public static void Run(Func main, Drivers drivers) => + public static void Run< + TSource1, + TSink1>( + Func< + IObservable, + IObservable> main, + Func, IObservable> firstDriver) => Kernel.Run( - sources => CycleMain( - sources, - drivers.Keys, - main), - (IObservable> sinks) => sinks.CallDrivers(drivers)); + sources => + { + var firstSources = sources.Split(); + + var firstSinks = main( + firstSources); + + return ObservableUnion.Merge( + firstSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver)); + + public static void Run< + TSource1, + TSink1>( + Func< + IObservable, + IObservable, + ( + IObservable NoneSinks, + IObservable FirstSinks)> main, + Func, IObservable> noneDriver, + Func, IObservable> firstDriver) => + Kernel.Run( + sources => + { + var ( + noneSources, + firstSources) = sources.Split(); + + var ( + noneSinks, + firstSinks) = main( + noneSources, + firstSources); + + return ObservableUnion.Merge( + noneSinks, + firstSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + noneDriver, + firstDriver)); public static void Run< TSource1, @@ -127,5 +174,518 @@ public static void Run< firstDriver, secondDriver, thirdDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6, + TSource7, + TSink7>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks, + IObservable SeventhSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver, + seventhDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6, + TSource7, + TSink7, + TSource8, + TSink8>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks, + IObservable SeventhSinks, + IObservable EighthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver, + seventhDriver, + eighthDriver)); + + public static void Run< + TSource1, + TSink1, + TSource2, + TSink2, + TSource3, + TSink3, + TSource4, + TSink4, + TSource5, + TSink5, + TSource6, + TSink6, + TSource7, + TSink7, + TSource8, + TSink8, + TSource9, + TSink9>( + Func< + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + IObservable, + ( + IObservable FirstSinks, + IObservable SecondSinks, + IObservable ThirdSinks, + IObservable FourthSinks, + IObservable FifthSinks, + IObservable SixthSinks, + IObservable SeventhSinks, + IObservable EighthSinks, + IObservable NinthSinks)> main, + Func, IObservable> firstDriver, + Func, IObservable> secondDriver, + Func, IObservable> thirdDriver, + Func, IObservable> fourthDriver, + Func, IObservable> fifthDriver, + Func, IObservable> sixthDriver, + Func, IObservable> seventhDriver, + Func, IObservable> eighthDriver, + Func, IObservable> ninthDriver) => + Kernel.Run( + sources => + { + var ( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources, + ninthSources) = sources.Split(); + + var ( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks, + ninthSinks) = main( + firstSources, + secondSources, + thirdSources, + fourthSources, + fifthSources, + sixthSources, + seventhSources, + eighthSources, + ninthSources); + + return ObservableUnion.Merge( + firstSinks, + secondSinks, + thirdSinks, + fourthSinks, + fifthSinks, + sixthSinks, + seventhSinks, + eighthSinks, + ninthSinks); + }, + (IObservable> sinks) => sinks.CallDrivers( + firstDriver, + secondDriver, + thirdDriver, + fourthDriver, + fifthDriver, + sixthDriver, + seventhDriver, + eighthDriver, + ninthDriver)); } } From 6a8bf24a8ea1263295ad378dc101876f09572b83 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Sat, 22 Apr 2023 21:46:51 -0400 Subject: [PATCH 36/42] Clean up issues --- Cycle.NET.Demo/Program.cs | 5 ++++- Cycle.NET/Runner.cs | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 9f2fc01..35bf0ae 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -6,7 +6,7 @@ namespace Cycle.NET.Demo { - class Program + internal static class Program { private static IObservable LogDriver(IObservable sinks) { @@ -24,6 +24,8 @@ private static IObservable LogDriver(IObservable sinks) private static IObservable KeyInputDriver(IObservable sinks) { + _ = sinks; + return Observable.Range(20, 5).Select(i => (object)i); } @@ -49,6 +51,7 @@ private static ( IObservable logSources, IObservable keyInputSources) { + _ = logSources; var logSink = keyInputSources; return ( diff --git a/Cycle.NET/Runner.cs b/Cycle.NET/Runner.cs index aea62fe..83ce79d 100644 --- a/Cycle.NET/Runner.cs +++ b/Cycle.NET/Runner.cs @@ -6,18 +6,30 @@ using System.Linq; using Cycle.NET.Extensions; using SdgApps.Common.DotnetSealedUnions; +using System.Runtime.Serialization; namespace Cycle.NET { + [Serializable] public class Streams : Dictionary> { public Streams() : base() { } + public Streams(Dictionary> value) : base(value) { } + + protected Streams( + SerializationInfo info, + StreamingContext context) : base(info, context) { } } + [Serializable] public class Drivers : Dictionary, IObservable>> { public Drivers() : base() { } + + protected Drivers( + SerializationInfo info, + StreamingContext context) : base(info, context) { } } public static class Runner From b0ffbfd0bfeff7a8d3122cbcb11bd5745907db9a Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Sat, 22 Apr 2023 21:52:00 -0400 Subject: [PATCH 37/42] Split demos --- Cycle.NET.Demo/Program.cs | 40 ---------------- .../Cycle.NET.UnionDemo.csproj | 16 +++++++ Cycle.NET.UnionDemo/Program.cs | 48 +++++++++++++++++++ Cycle.NET.sln | 14 ++++-- 4 files changed, 74 insertions(+), 44 deletions(-) create mode 100644 Cycle.NET.UnionDemo/Cycle.NET.UnionDemo.csproj create mode 100644 Cycle.NET.UnionDemo/Program.cs diff --git a/Cycle.NET.Demo/Program.cs b/Cycle.NET.Demo/Program.cs index 35bf0ae..4947afe 100644 --- a/Cycle.NET.Demo/Program.cs +++ b/Cycle.NET.Demo/Program.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Reactive.Linq; -using Cycle.NET.Extensions; -using SdgApps.Common.DotnetSealedUnions; namespace Cycle.NET.Demo { @@ -15,13 +13,6 @@ private static IObservable LogDriver(IObservable sinks) return Observable.Empty(); } - private static IObservable LogDriver(IObservable sinks) - { - sinks.Subscribe(i => Console.WriteLine("Log " + i.ToString())); - - return Observable.Empty(); - } - private static IObservable KeyInputDriver(IObservable sinks) { _ = sinks; @@ -29,11 +20,6 @@ private static IObservable KeyInputDriver(IObservable sinks) return Observable.Range(20, 5).Select(i => (object)i); } - private static IObservable KeyInputDriver(IObservable sinks) - { - return Observable.Range(20, 5); - } - private static Streams CycleMain(Streams sources) { var sinks = new Streams @@ -43,22 +29,6 @@ private static Streams CycleMain(Streams sources) return sinks; } - - private static ( - IObservable LogSinks, - IObservable KeyInputSinks) - CycleMain( - IObservable logSources, - IObservable keyInputSources) - { - _ = logSources; - var logSink = keyInputSources; - - return ( - logSink, - Observable.Empty()); - } - static void Main(string[] args) { var drivers = new Drivers @@ -67,16 +37,6 @@ static void Main(string[] args) { "keys", KeyInputDriver } }; Runner.Run(CycleMain, drivers); - - Runner.Run( - ( - IObservable logSources, - IObservable keyInputSources) => CycleMain( - logSources, - keyInputSources), - LogDriver, - KeyInputDriver); - Console.ReadLine(); } } diff --git a/Cycle.NET.UnionDemo/Cycle.NET.UnionDemo.csproj b/Cycle.NET.UnionDemo/Cycle.NET.UnionDemo.csproj new file mode 100644 index 0000000..6ac46b6 --- /dev/null +++ b/Cycle.NET.UnionDemo/Cycle.NET.UnionDemo.csproj @@ -0,0 +1,16 @@ + + + + Exe + net7.0 + + + + + + + + + + + diff --git a/Cycle.NET.UnionDemo/Program.cs b/Cycle.NET.UnionDemo/Program.cs new file mode 100644 index 0000000..5efda0b --- /dev/null +++ b/Cycle.NET.UnionDemo/Program.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Linq; + +namespace Cycle.NET.Demo +{ + internal static class Program + { + private static IObservable LogDriver(IObservable sinks) + { + sinks.Subscribe(i => Console.WriteLine("Log " + i.ToString())); + + return Observable.Empty(); + } + + private static IObservable KeyInputDriver(IObservable sinks) + { + return Observable.Range(20, 5); + } + + private static ( + IObservable LogSinks, + IObservable KeyInputSinks) + CycleMain( + IObservable logSources, + IObservable keyInputSources) + { + _ = logSources; + var logSink = keyInputSources; + + return ( + logSink, + Observable.Empty()); + } + static void Main(string[] args) + { + Runner.Run( + ( + IObservable logSources, + IObservable keyInputSources) => CycleMain( + logSources, + keyInputSources), + LogDriver, + KeyInputDriver); + Console.ReadLine(); + } + } +} diff --git a/Cycle.NET.sln b/Cycle.NET.sln index 5cc5ff2..4463e49 100644 --- a/Cycle.NET.sln +++ b/Cycle.NET.sln @@ -1,11 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26621.2 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33516.290 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cycle.NET", "Cycle.NET\Cycle.NET.csproj", "{6A0D9074-CE78-4217-A7D6-07554439A5BF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cycle.NET", "Cycle.NET\Cycle.NET.csproj", "{6A0D9074-CE78-4217-A7D6-07554439A5BF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cycle.NET.Demo", "Cycle.NET.Demo\Cycle.NET.Demo.csproj", "{E0B049E9-C963-4627-872B-56F6BCE5728C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cycle.NET.Demo", "Cycle.NET.Demo\Cycle.NET.Demo.csproj", "{E0B049E9-C963-4627-872B-56F6BCE5728C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cycle.NET.UnionDemo", "Cycle.NET.UnionDemo\Cycle.NET.UnionDemo.csproj", "{65F17018-1196-4153-A2D4-9711768A030B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,10 @@ Global {E0B049E9-C963-4627-872B-56F6BCE5728C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0B049E9-C963-4627-872B-56F6BCE5728C}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0B049E9-C963-4627-872B-56F6BCE5728C}.Release|Any CPU.Build.0 = Release|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65F17018-1196-4153-A2D4-9711768A030B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From dfb7041af7520e62e0ae59a09abef75159862926 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Tue, 25 Apr 2023 02:57:45 -0400 Subject: [PATCH 38/42] Add toggle demo --- Cycle.NET.UnionDemo/Program.cs | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Cycle.NET.UnionDemo/Program.cs b/Cycle.NET.UnionDemo/Program.cs index 5efda0b..077c15a 100644 --- a/Cycle.NET.UnionDemo/Program.cs +++ b/Cycle.NET.UnionDemo/Program.cs @@ -6,6 +6,7 @@ namespace Cycle.NET.Demo { internal static class Program { +#if ORIG_DEMO private static IObservable LogDriver(IObservable sinks) { sinks.Subscribe(i => Console.WriteLine("Log " + i.ToString())); @@ -44,5 +45,41 @@ static void Main(string[] args) KeyInputDriver); Console.ReadLine(); } +#else + private static IObservable DomDriver(IObservable sinks) + { + sinks.Subscribe(Console.WriteLine); + + return new List + { + false, + true, + false, + true, + } + .ToObservable(); + } + + private static + IObservable + CycleMain( + IObservable domSources) + { + var domSink = + domSources + .Select(toggled => toggled ? "ON" : "off"); + + return domSink; + } + static void Main(string[] args) + { + Runner.Run( + ( + IObservable domSources) => CycleMain( + domSources), + DomDriver); + Console.ReadLine(); + } +#endif } } From f79ec21f2d69c3ae322205ab58f812743b96ae04 Mon Sep 17 00:00:00 2001 From: Nathaniel Bond Date: Wed, 26 Apr 2023 00:54:04 -0400 Subject: [PATCH 39/42] Add demo app project --- Cycle.NET.ToggleDemo/App.xaml | 14 + Cycle.NET.ToggleDemo/App.xaml.cs | 12 + Cycle.NET.ToggleDemo/AppShell.xaml | 14 + Cycle.NET.ToggleDemo/AppShell.xaml.cs | 10 + .../Cycle.NET.ToggleDemo.csproj | 55 +++ Cycle.NET.ToggleDemo/MainPage.xaml | 41 ++ Cycle.NET.ToggleDemo/MainPage.xaml.cs | 24 ++ Cycle.NET.ToggleDemo/MauiProgram.cs | 25 ++ .../Platforms/Android/AndroidManifest.xml | 6 + .../Platforms/Android/MainActivity.cs | 11 + .../Platforms/Android/MainApplication.cs | 16 + .../Android/Resources/values/colors.xml | 6 + .../Platforms/MacCatalyst/AppDelegate.cs | 10 + .../Platforms/MacCatalyst/Info.plist | 30 ++ .../Platforms/MacCatalyst/Program.cs | 16 + Cycle.NET.ToggleDemo/Platforms/Tizen/Main.cs | 17 + .../Platforms/Tizen/tizen-manifest.xml | 15 + .../Platforms/Windows/App.xaml | 8 + .../Platforms/Windows/App.xaml.cs | 24 ++ .../Platforms/Windows/Package.appxmanifest | 46 ++ .../Platforms/Windows/app.manifest | 15 + .../Platforms/iOS/AppDelegate.cs | 10 + Cycle.NET.ToggleDemo/Platforms/iOS/Info.plist | 32 ++ Cycle.NET.ToggleDemo/Platforms/iOS/Program.cs | 16 + .../Properties/launchSettings.json | 8 + .../Resources/AppIcon/appicon.svg | 4 + .../Resources/AppIcon/appiconfg.svg | 8 + .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 107140 bytes .../Resources/Fonts/OpenSans-Semibold.ttf | Bin 0 -> 111068 bytes .../Resources/Images/dotnet_bot.svg | 93 ++++ .../Resources/Raw/AboutAssets.txt | 15 + .../Resources/Splash/splash.svg | 8 + .../Resources/Styles/Colors.xaml | 44 ++ .../Resources/Styles/Styles.xaml | 405 ++++++++++++++++++ Cycle.NET.sln | 10 +- 35 files changed, 1067 insertions(+), 1 deletion(-) create mode 100644 Cycle.NET.ToggleDemo/App.xaml create mode 100644 Cycle.NET.ToggleDemo/App.xaml.cs create mode 100644 Cycle.NET.ToggleDemo/AppShell.xaml create mode 100644 Cycle.NET.ToggleDemo/AppShell.xaml.cs create mode 100644 Cycle.NET.ToggleDemo/Cycle.NET.ToggleDemo.csproj create mode 100644 Cycle.NET.ToggleDemo/MainPage.xaml create mode 100644 Cycle.NET.ToggleDemo/MainPage.xaml.cs create mode 100644 Cycle.NET.ToggleDemo/MauiProgram.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/Android/AndroidManifest.xml create mode 100644 Cycle.NET.ToggleDemo/Platforms/Android/MainActivity.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/Android/MainApplication.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/Android/Resources/values/colors.xml create mode 100644 Cycle.NET.ToggleDemo/Platforms/MacCatalyst/AppDelegate.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Info.plist create mode 100644 Cycle.NET.ToggleDemo/Platforms/MacCatalyst/Program.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/Tizen/Main.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/Tizen/tizen-manifest.xml create mode 100644 Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml create mode 100644 Cycle.NET.ToggleDemo/Platforms/Windows/App.xaml.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/Windows/Package.appxmanifest create mode 100644 Cycle.NET.ToggleDemo/Platforms/Windows/app.manifest create mode 100644 Cycle.NET.ToggleDemo/Platforms/iOS/AppDelegate.cs create mode 100644 Cycle.NET.ToggleDemo/Platforms/iOS/Info.plist create mode 100644 Cycle.NET.ToggleDemo/Platforms/iOS/Program.cs create mode 100644 Cycle.NET.ToggleDemo/Properties/launchSettings.json create mode 100644 Cycle.NET.ToggleDemo/Resources/AppIcon/appicon.svg create mode 100644 Cycle.NET.ToggleDemo/Resources/AppIcon/appiconfg.svg create mode 100644 Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 Cycle.NET.ToggleDemo/Resources/Fonts/OpenSans-Semibold.ttf create mode 100644 Cycle.NET.ToggleDemo/Resources/Images/dotnet_bot.svg create mode 100644 Cycle.NET.ToggleDemo/Resources/Raw/AboutAssets.txt create mode 100644 Cycle.NET.ToggleDemo/Resources/Splash/splash.svg create mode 100644 Cycle.NET.ToggleDemo/Resources/Styles/Colors.xaml create mode 100644 Cycle.NET.ToggleDemo/Resources/Styles/Styles.xaml diff --git a/Cycle.NET.ToggleDemo/App.xaml b/Cycle.NET.ToggleDemo/App.xaml new file mode 100644 index 0000000..f1e7bf1 --- /dev/null +++ b/Cycle.NET.ToggleDemo/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/Cycle.NET.ToggleDemo/App.xaml.cs b/Cycle.NET.ToggleDemo/App.xaml.cs new file mode 100644 index 0000000..3afe14b --- /dev/null +++ b/Cycle.NET.ToggleDemo/App.xaml.cs @@ -0,0 +1,12 @@ +namespace Cycle.NET.ToggleDemo +{ + public partial class App : Application + { + public App() + { + InitializeComponent(); + + MainPage = new AppShell(); + } + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/AppShell.xaml b/Cycle.NET.ToggleDemo/AppShell.xaml new file mode 100644 index 0000000..28dfa95 --- /dev/null +++ b/Cycle.NET.ToggleDemo/AppShell.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/Cycle.NET.ToggleDemo/AppShell.xaml.cs b/Cycle.NET.ToggleDemo/AppShell.xaml.cs new file mode 100644 index 0000000..ec2f41c --- /dev/null +++ b/Cycle.NET.ToggleDemo/AppShell.xaml.cs @@ -0,0 +1,10 @@ +namespace Cycle.NET.ToggleDemo +{ + public partial class AppShell : Shell + { + public AppShell() + { + InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/Cycle.NET.ToggleDemo/Cycle.NET.ToggleDemo.csproj b/Cycle.NET.ToggleDemo/Cycle.NET.ToggleDemo.csproj new file mode 100644 index 0000000..a2765a8 --- /dev/null +++ b/Cycle.NET.ToggleDemo/Cycle.NET.ToggleDemo.csproj @@ -0,0 +1,55 @@ + + + + net7.0-android;net7.0-ios;net7.0-maccatalyst + $(TargetFrameworks);net7.0-windows10.0.19041.0 + + + Exe + Cycle.NET.ToggleDemo + true + true + enable + + + Cycle.NET.ToggleDemo + + + com.companyname.cycle.net.toggledemo + 890c6119-9c69-4ea5-9c90-82044e4cd5d2 + + + 1.0 + 1 + + 11.0 + 13.1 + 21.0 + 10.0.17763.0 + 10.0.17763.0 + 6.5 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Cycle.NET.ToggleDemo/MainPage.xaml b/Cycle.NET.ToggleDemo/MainPage.xaml new file mode 100644 index 0000000..357fc94 --- /dev/null +++ b/Cycle.NET.ToggleDemo/MainPage.xaml @@ -0,0 +1,41 @@ + + + + + + + + +