Skip to content

Commit 6881aad

Browse files
authored
feat(iterator): implemented ReplayableSequence, Window, Flow, and AyncFlow to demonstrate iterator pattern. (#18)
1 parent fe6916f commit 6881aad

20 files changed

Lines changed: 2116 additions & 194 deletions

File tree

README.md

Lines changed: 254 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,266 @@ if (parse.Execute("123", out var n))
6262
Console.WriteLine(n); // 123
6363
```
6464

65+
A forkable, lookahead **ReplayableSequence** (Iterator+):
66+
67+
```csharp
68+
using PatternKit.Behavioral.Iterator;
69+
70+
var seq = ReplayableSequence<int>.From(Enumerable.Range(1, 5));
71+
var c = seq.GetCursor();
72+
73+
// Look ahead without consuming
74+
var first = c.Lookahead(0).OrDefault(); // 1
75+
var third = c.Lookahead(2).OrDefault(); // 3
76+
77+
// Consume immutably (returns next cursor)
78+
if (c.TryNext(out var v1, out var c2) && c2.TryNext(out var v2, out var c3))
79+
{
80+
// v1 = 1, v2 = 2; c3 now points at 3
81+
}
82+
83+
// Fork speculative branch
84+
var fork = c3.Fork();
85+
if (fork.TryNext(out var v3, out fork) && fork.TryNext(out var v4, out fork))
86+
{
87+
// fork saw 3,4 while c3 is still at 3
88+
}
89+
90+
// LINQ over a cursor (non-destructive to original cursor)
91+
var evens = c3.Where(x => x % 2 == 0).ToList(); // [2,4] using a snapshot enumeration
92+
```
93+
94+
### WindowSequence (sliding / striding windows)
95+
```csharp
96+
using PatternKit.Behavioral.Iterator;
97+
98+
// Full sliding windows (size 3, stride 1)
99+
var slides = Enumerable.Range(1,7)
100+
.Windows(size:3)
101+
.Select(w => string.Join(',', w.ToArray()))
102+
.ToList(); // ["1,2,3","2,3,4","3,4,5","4,5,6","5,6,7"]
103+
104+
// Stride 2 (skip one between window starts)
105+
var stride = Enumerable.Range(1,9)
106+
.Windows(size:4, stride:2)
107+
.Select(w => string.Join('-', w.ToArray()))
108+
.ToList(); // ["1-2-3-4","3-4-5-6","5-6-7-8"]
109+
110+
// Include trailing partial
111+
var partial = new[]{1,2,3,4,5}
112+
.Windows(size:3, stride:3, includePartial:true)
113+
.Select(w => (Vals: w.ToArray(), w.IsPartial))
114+
.ToList(); // [ ([1,2,3], false), ([4,5], true) ]
115+
116+
// Reuse buffer (zero alloc per full window) – copy if you persist
117+
var reused = Enumerable.Range(1,6)
118+
.Windows(size:3, reuseBuffer:true)
119+
.Select(w => w.ToArray()) // snapshot copy each window
120+
.ToList();
121+
```
122+
123+
---
124+
125+
## 📘 Pattern Quick Reference
126+
Tiny, copy‑paste friendly snippets for the most common patterns. Each builds an immutable, hot‑path friendly artifact.
127+
128+
### ActionChain (middleware style rule pack)
129+
```csharp
130+
using PatternKit.Behavioral.Chain;
131+
132+
var log = new List<string>();
133+
var chain = ActionChain<HttpRequest>.Create()
134+
.When((in r) => r.Path.StartsWith("/admin") && !r.Headers.ContainsKey("Authorization"))
135+
.ThenStop(r => log.Add("deny"))
136+
.When((in r) => r.Headers.ContainsKey("X-Request-Id"))
137+
.ThenContinue(r => log.Add($"req={r.Headers["X-Request-Id"]}"))
138+
.Finally((in r, next) => { log.Add($"{r.Method} {r.Path}"); next(in r); })
139+
.Build();
140+
141+
chain.Execute(new("GET","/health", new Dictionary<string,string>()));
142+
```
143+
144+
### ResultChain (first-match value producer with fallback)
145+
```csharp
146+
using PatternKit.Behavioral.Chain;
147+
148+
public readonly record struct Request(string Method, string Path);
149+
public readonly record struct Response(int Status, string Body);
150+
151+
var router = ResultChain<Request, Response>.Create()
152+
.When(static (in r) => r.Method == "GET" && r.Path == "/health")
153+
.Then(r => new Response(200, "OK"))
154+
.When(static (in r) => r.Method == "GET" && r.Path.StartsWith("/users/"))
155+
.Then(r => new Response(200, $"user:{r.Path[7..]}"))
156+
// default / not found tail (only runs if no earlier handler produced)
157+
.Finally(static (in _, out Response? res, _) => { res = new Response(404, "not found"); return true; })
158+
.Build();
159+
160+
router.Execute(new Request("GET", "/health"), out var ok); // ok.Status = 200
161+
router.Execute(new Request("GET", "/users/alice"), out var u); // 200, Body = user:alice
162+
router.Execute(new Request("GET", "/missing"), out var nf); // 404, Body = not found
163+
```
164+
165+
### Strategy (first matching branch)
166+
```csharp
167+
using PatternKit.Behavioral.Strategy;
168+
var classify = Strategy<int,string>.Create()
169+
.When(i => i > 0).Then(_ => "positive")
170+
.When(i => i < 0).Then(_ => "negative")
171+
.Default(_ => "zero")
172+
.Build();
173+
var result = classify.Execute(-5); // negative
174+
```
175+
176+
### TryStrategy (first success wins parsing)
177+
```csharp
178+
var parse = TryStrategy<string,int>.Create()
179+
.Always((in string s, out int v) => int.TryParse(s, out v))
180+
.Finally((in string _, out int v) => { v = 0; return true; })
181+
.Build();
182+
parse.Execute("42", out var n); // n=42
183+
```
184+
185+
### ActionStrategy (multi-fire side‑effects)
186+
```csharp
187+
using PatternKit.Behavioral.Strategy;
188+
var audit = new List<string>();
189+
var strat = ActionStrategy<int>.Create()
190+
.When(i => i % 2 == 0).Then(i => audit.Add($"even:{i}"))
191+
.When(i => i > 10).Then(i => audit.Add($"big:{i}"))
192+
.Build();
193+
strat.Execute(12); // adds even:12, big:12
194+
```
195+
196+
### AsyncStrategy (await external work)
197+
```csharp
198+
var asyncStrat = AsyncStrategy<string,string>.Create()
199+
.When(s => s.StartsWith("http"))
200+
.Then(async s => await Task.FromResult("url"))
201+
.Default(async s => await Task.FromResult("text"))
202+
.Build();
203+
var kind = await asyncStrat.Execute("http://localhost");
204+
```
205+
206+
### BranchBuilder (first-match router)
207+
```csharp
208+
using PatternKit.Creational.Builder;
209+
210+
// Define delegate shapes (predicates take `in` param for struct-friendliness)
211+
public delegate bool IntPred(in int x);
212+
public delegate string IntHandler(in int x);
213+
214+
var router = BranchBuilder<IntPred, IntHandler>.Create()
215+
.Add(static (in int v) => v < 0, static (in int v) => "neg")
216+
.Add(static (in int v) => v > 0, static (in int v) => "pos")
217+
.Default(static (in int _) => "zero")
218+
.Build(
219+
fallbackDefault: static (in int _) => "zero",
220+
projector: static (preds, handlers, hasDefault, def) =>
221+
{
222+
// Project into a single dispatch function
223+
return (Func<int, string>)(x =>
224+
{
225+
for (int i = 0; i < preds.Length; i++)
226+
if (preds[i](in x))
227+
return handlers[i](in x);
228+
return def(in x);
229+
});
230+
});
231+
232+
var a = router(-5); // "neg"
233+
var b = router(10); // "pos"
234+
var c = router(0); // "zero"
235+
```
236+
237+
### ChainBuilder (collect -> project)
238+
```csharp
239+
using PatternKit.Creational.Builder;
240+
241+
var log = new List<string>();
242+
var pipeline = ChainBuilder<Action<string>>.Create()
243+
.Add(static s => log.Add($"A:{s}"))
244+
.AddIf(true, static s => log.Add($"B:{s}"))
245+
.Add(static s => log.Add($"C:{s}"))
246+
.Build(actions => (Action<string>)(msg =>
247+
{
248+
foreach (var a in actions) a(msg);
249+
}));
250+
251+
pipeline("run");
252+
// log => ["A:run", "B:run", "C:run"]
253+
```
254+
255+
### Composer (functional state accumulation)
256+
```csharp
257+
using PatternKit.Creational.Builder;
258+
259+
public readonly record struct PersonState(string? Name, int Age);
260+
public sealed record Person(string Name, int Age);
261+
262+
var person = Composer<PersonState, Person>
263+
.New(static () => default)
264+
.With(static s => s with { Name = "Ada" })
265+
.With(static s => s with { Age = 30 })
266+
.Require(static s => string.IsNullOrWhiteSpace(s.Name) ? "Name required" : null)
267+
.Build(static s => new Person(s.Name!, s.Age));
268+
```
269+
270+
### MutableBuilder (imperative mutations + validation)
271+
```csharp
272+
using PatternKit.Creational.Builder;
273+
274+
public sealed class Options
275+
{
276+
public string? Host { get; set; }
277+
public int Port { get; set; }
278+
}
279+
280+
var opts = MutableBuilder<Options>.New(static () => new Options())
281+
.With(static o => o.Host = "localhost")
282+
.With(static o => o.Port = 8080)
283+
.RequireNotEmpty(static o => o.Host, nameof(Options.Host))
284+
.RequireRange(static o => o.Port, 1, 65535, nameof(Options.Port))
285+
.Build();
286+
```
287+
288+
### Prototype (clone + mutate)
289+
```csharp
290+
using PatternKit.Creational.Prototype;
291+
292+
public sealed class User { public string Role { get; set; } = "user"; public bool Active { get; set; } = true; }
293+
294+
// Single prototype
295+
var proto = Prototype<User>.Create(
296+
source: new User { Role = "user", Active = true },
297+
cloner: static (in User u) => new User { Role = u.Role, Active = u.Active })
298+
.With(static u => u.Active = false) // default mutation applied to every clone
299+
.Build();
300+
301+
var admin = proto.Create(u => u.Role = "admin"); // clone + extra mutation
302+
303+
// Registry of prototypes
304+
var registry = Prototype<string, User>.Create()
305+
.Map("basic", new User { Role = "user", Active = true }, static (in User u) => new User { Role = u.Role, Active = u.Active })
306+
.Map("admin", new User { Role = "admin", Active = true }, static (in User u) => new User { Role = u.Role, Active = u.Active })
307+
.Mutate("admin", static u => u.Active = true) // append mutation to admin family
308+
.Default(new User { Role = "guest", Active = false }, static (in User u) => new User { Role = u.Role, Active = u.Active })
309+
.Build();
310+
311+
var guest = registry.Create("missing-key"); // falls back to default (guest)
312+
```
313+
65314
---
66315

67316
## 📦 Patterns (Planned & In Progress)
68317

69318
PatternKit will grow to cover **Creational**, **Structural**, and **Behavioral** patterns with fluent, discoverable APIs:
70319

71-
| Category | Patterns ✓ = implemented |
72-
| -------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
73-
| **Creational** | [Factory](docs/patterns/creational/factory/factory.md) ✓ • [Composer](docs/patterns/creational/builder/composer.md) ✓ • [ChainBuilder](docs/patterns/creational/builder/chainbuilder.md) ✓ • [BranchBuilder](docs/patterns/creational/builder/chainbuilder.md) ✓ • [MutableBuilder](docs/patterns/creational/builder/mutablebuilder.md) ✓ • [Prototype](docs/patterns/creational/prototype/prototype.md) ✓ • [Singleton](docs/patterns/creational/singleton/singleton.md)|
74-
| **Structural** | [Adapter](docs/patterns/structural/adapter/fluent-adapter.md) ✓ • [Bridge](docs/patterns/structural/bridge/bridge.md) ✓ • [Composite](docs/patterns/structural/composite/composite.md) ✓ • Decorator (planned) • Facade (planned) • Flyweight (planned) • Proxy (planned) |
75-
| **Behavioral** | [Strategy](docs/patterns/behavioral/strategy/strategy.md) ✓ • [TryStrategy](docs/patterns/behavioral/strategy/trystrategy.md) ✓ • [ActionStrategy](docs/patterns/behavioral/strategy/actionstrategy.md) ✓ • [ActionChain](docs/patterns/behavioral/chain/actionchain.md) ✓ • [ResultChain](docs/patterns/behavioral/chain/resultchain.md) ✓ • [Command](docs/patterns/behavioral/command/command.md) ✓ • Iterator (planned) • [Mediator](docs/behavioral/mediator/mediator.md) ✓ • Memento (planned) • Observer (planned) • State (planned) • Template Method (planned) • Visitor (planned) |
320+
| Category | Patterns ✓ = implemented |
321+
| -------------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
322+
| **Creational** | [Factory](docs/patterns/creational/factory/factory.md) ✓ • [Composer](docs/patterns/creational/builder/composer.md) ✓ • [ChainBuilder](docs/patterns/creational/builder/chainbuilder.md) ✓ • [BranchBuilder](docs/patterns/creational/builder/chainbuilder.md) ✓ • [MutableBuilder](docs/patterns/creational/builder/mutablebuilder.md) ✓ • [Prototype](docs/patterns/creational/prototype/prototype.md) ✓ • [Singleton](docs/patterns/creational/singleton/singleton.md) |
323+
| **Structural** | [Adapter](docs/patterns/structural/adapter/fluent-adapter.md) ✓ • [Bridge](docs/patterns/structural/bridge/bridge.md) ✓ • [Composite](docs/patterns/structural/composite/composite.md) ✓ • Decorator (planned) • Facade (planned) • Flyweight (planned) • Proxy (planned) |
324+
| **Behavioral** | [Strategy](docs/patterns/behavioral/strategy/strategy.md) ✓ • [TryStrategy](docs/patterns/behavioral/strategy/trystrategy.md) ✓ • [ActionStrategy](docs/patterns/behavioral/strategy/actionstrategy.md) ✓ • [ActionChain](docs/patterns/behavioral/chain/actionchain.md) ✓ • [ResultChain](docs/patterns/behavioral/chain/resultchain.md) ✓ • [ReplayableSequence](docs/patterns/behavioral/iterator/replayablesequence.md) ✓ • [WindowSequence](docs/patterns/behavioral/iterator/windowsequence.md) • Command (planned) • Mediator (planned) • Memento (planned) • Observer (planned) • State (planned) • Template Method (planned) • Visitor (planned) |
76325

77326
Each pattern will ship with:
78327

0 commit comments

Comments
 (0)