Swolfkrow 0.4.0
dotnet add package Swolfkrow --version 0.4.0
NuGet\Install-Package Swolfkrow -Version 0.4.0
<PackageReference Include="Swolfkrow" Version="0.4.0" />
paket add Swolfkrow --version 0.4.0
#r "nuget: Swolfkrow, 0.4.0"
// Install Swolfkrow as a Cake Addin
#addin nuget:?package=Swolfkrow&version=0.4.0
// Install Swolfkrow as a Cake Tool
#tool nuget:?package=Swolfkrow&version=0.4.0
Swolfkrow
Swolfkrow (pronounced /ˈwɜː(r)kˌfləʊs/) is a Domain Specific Language (DSL) designed to declaratively compose asynchronous workflows through a fluent API.
Asynchronous workflows
In the context of Swolfkrow, asynchronous workflows can be understood as asynchronous computations that yield an asynchronous stream of events signaling progress, outcomes, and errors.
In practice, asynchronous workflows are objects that implement the IAsyncEnumerable<out T>
interface, with a few semantic constraints:
- The generic type
T
, namedTEvent
across the Swolfkrow library, represents the base type from which all events potentially yielded by the asynchronous workflow derive. - Yielded
TEvent
objects are assumed to describe relevant events occured during the asynchronous workflow's execution. - When enumerated, asynchronous workflows may execute arbitrary asynchronous logic in between consecutively yielded
TEvent
objects.
Swolfkrow provides the programmatic glue required to declaratively compose simpler asynchronous workflows together into more complex ones. It also provides support to build asynchronous workflows from other primitives, like IEnumerable<TEvent>
, Task<TEvent>
, or ValueTask<TEvent>
.
Fluent API
Swolfkrow's fluent API can be visualized as a state machine, where the (arguably less relevant) supporting classes are states, and the composition operators are transitions:
stateDiagram-v2
state "Workflow" as workflow
state "Trigger" as trigger
[*] --> workflow : Workflow.Start(...)
workflow --> [*] : IAsyncEnumerable
workflow --> workflow : .Then(...)\n.While(...)\n.Until(...)\n.Do(...)
workflow --> trigger : .When(...)
%%coming soon!
%%trigger --> trigger : .Times(...)
trigger --> workflow : .Then(...)
trigger --> workflow : .Do(...)
The entry point to the DSL is always one of the many Workflow.Start
factory method overloads, all of which return an initial Workflow<Event>
instance.
The Workflow<TEvent>
class exposes a number of operators that perform single-step compositions and return a new Workflow<TEvent>
instance:
- Continuations:
workflow.Then(...)
- Side-effects:
workflow.Do(...)
- Interruptions:
workflow.While(...)
,workflow.Until(...)
The Workflow<TEvent>
class also exposes a Workflow<TEvent>.When
operator that enables two-step triggered compositions:
- Triggered continuations:
workflow.When(...).Then(...)
- Triggered side-effects:
workflow.When(...).Do(...)
Triggered compositions rely on an intermediate Trigger<TEvent, ...>
instance produced by the initial call to Workflow<TEvent>.When
.
The API can be exited after any of the operators that return a Workflow<TEvent>
instance, given that the Workflow<TEvent>
class itself implements IAsyncEnumerable<TEvent>
.
The Workflow<TEvent> class
The Workflow<TEvent>
class provides a semantic anchor with a self-descriptive name, as well as a convenient container for the fluent API operators. It is meaningful only during the composition of asynchronous workflows and uninteresting outside that context. Composed asynchronous workflows can be exposed directly as IAsyncEnumerable<TEvent>
instances.
Overview
The following subsections briefly describe the different types of asynchronous workflow compositions that can be performed through Swolfkrow's fluent API operators.
Initiations
The family of Start
method overloads provides a single entry point to the DSL:
record EventBase(string Description);
IAsyncEnumerable<EventBase> Step1() { ... }
IAsyncEnumerable<EventBase> ComposeWorkflow()
=> Workflow
.Start(Step1);
Continuations
Asynchronous workflows can be composed as sequences of simpler workflows, where one workflow starts when the previous one finishes yielding events:
record EventBase(string Description);
IAsyncEnumerable<EventBase> Step1() { ... }
IAsyncEnumerable<EventBase> Step2() { ... }
IAsyncEnumerable<EventBase> ComposedWorkflow()
=> Workflow
.Start(Step1)
.Then(Step2);
Stateful continuations
Workflow continuations can be based on state explicitly folded from the events yielded by the workflow:
record EventBase(string Description);
IAsyncEnumerable<EventBase> Step1() { ... }
IAsyncEnumerable<EventBase> Step2(int someInfo) { ... }
IAsyncEnumerable<EventBase> ComposedWorkflow()
=> Workflow
.Start(Step1)
.Then(
createContinuation: currentState => currentState * 2,
computeNextState: (currentState, nextEvent) => currentState + 1,
initialState: 0);
Intercalations
Asynchronous workflows can be intercalated and executed in the middle of other asynchronous workflows, triggered by events of a specific type and/or satisfying a predicate:
record EventBase(string Description);
record SomethingHappened(string Description) : EventBase(Description);
IAsyncEnumerable<EventBase> Step1() { ... }
IAsyncEnumerable<EventBase> Step2(SomethingHappened somethingHappened) { ... }
IAsyncEnumerable<EventBase> ComposedWorkflow()
=> Workflow
.Start(Step1)
.When<SomethingHappened>().Then(Step2);
Side effects
Side effects can be deliberately injected into an asynchronous workflow:
record EventBase(string Description);
IAsyncEnumerable<EventBase> Step1() { ... }
IAsyncEnumerable<EventBase> Step2() { ... }
void LogEvent(EventBase someEvent)
=> Console.WriteLine($"Something happened: {EventBase}")
IAsyncEnumerable<EventBase> ComposedWorkflow()
=> Workflow
.Start(Step1)
.Then(Step2)
.Do(LogEvent);
Interruptions
Asynchronous workflows can be interrupted based on a condition computed on each of the yielded events:
public record EventBase(string Description);
public record SomeError(string Description, Exception Exception) : EventBase(Description);
IAsyncEnumerable<EventBase> Step1() { ... }
IAsyncEnumerable<EventBase> Step2(int someInfo) { ... }
bool IsError(EventBase nextEvent) => nextEvent is SomeError;
IAsyncEnumerable<EventBase> ComposedWorkflow()
=> Workflow
.Start(Step1)
.Then(Step2, 42)
.Until(IsError);
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Linq.Async (>= 6.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.4.0 | 157 | 9/1/2023 |
0.3.2 | 144 | 7/22/2023 |
0.3.1 | 171 | 7/3/2023 |
0.3.0 | 156 | 7/2/2023 |
0.2.5 | 145 | 6/18/2023 |
0.2.4 | 146 | 6/12/2023 |
0.2.3 | 164 | 6/9/2023 |
0.2.2 | 161 | 6/6/2023 |
0.2.1 | 135 | 6/4/2023 |
0.2.0 | 147 | 5/29/2023 |
0.1.3 | 150 | 5/29/2023 |
0.1.2 | 152 | 5/25/2023 |
0.1.1 | 155 | 5/22/2023 |
0.1.0 | 150 | 5/21/2023 |
# 0.4.0 (2023-09-01)
- Target .NET Standard 2.0
# 0.3.2 (2023-07-22)
- Added interruption overloads with additional arguments.
# 0.3.1 (2023-07-04)
- Fixed the return types of some public api methods.
# 0.3.0 (2023-07-02)
- BREAKING CHANGE: rewritten extension methods as members of a newly added `Workflow<TEvent>` class.
- BREAKING CHANGE: rewritten intercalations as two-step compositions.
# 0.2.5 (2023-06-19)
- Added intercalation overloads.
# 0.2.4 (2023-06-13)
- Added interruptions.
# 0.2.3 (2023-06-10)
- Full review of the comment-based documentation.
- Added `IEnumerable` based overloads of start, continuation, and event continuation operators.
# 0.2.2 (2023-06-07)
- Added synchronous continuations.
# 0.2.1 (2023-06-04)
- Start/continue asynchronous workflows from `Task` and `ValueTask`
# 0.2.0 (2023-05-29)
- Simplified stateful continuations.
# 0.1.3 (2023-05-26)
- Added event continuations
# 0.1.2 (2023-05-25)
- Added `Start` and `Then` overloads with additional arguments
# 0.1.1 (2023-05-22)
- Added stateful continuations.
# 0.1.0 (2023-05-21)
- Initial Version.
- Added workflow continuations.