Executables 1.0.0

dotnet add package Executables --version 1.0.0
                    
NuGet\Install-Package Executables -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Executables" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Executables" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Executables" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Executables --version 1.0.0
                    
#r "nuget: Executables, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Executables@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Executables&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Executables&version=1.0.0
                    
Install as a Cake Tool

Executables

Build application logic once - run it with different runtime behavior.

Executables is a composable .NET library for modeling application behavior as reusable, strongly typed operations.

It separates:

  • what your code does: executable composition,
  • how your code runs: runtime policies, branching, context, error handling, and execution control.

That means the same logic can be reused across different application boundaries and executed with different runtime rules without rewriting the core flow.

Why Executables

In real applications, behavior often gets scattered across many places:

  • business logic in services,
  • validation in validators,
  • retries and timeouts in wrappers,
  • error handling in middleware,
  • branching logic inside large methods,
  • commands, queries, and event handlers as separate API shapes.

Over time, that makes execution flow harder to reuse, test, adapt, and reason about.

Executables gives you one explicit composition model for that behavior. It becomes useful when direct method calls are no longer enough because the same logic needs to be:

  • composable,
  • reusable across multiple entry points,
  • decorated with runtime rules,
  • strongly typed from input to output,
  • easy to test in isolation.

Typical fits include:

  • application-layer orchestration,
  • validation-heavy workflows,
  • query and command pipelines,
  • explicit branching and fallback flows,
  • policy-based execution with retry, timeout, guards, and context propagation.

With it, you can define logic once, compose it from small typed steps, expose it as a query, command, event subscriber, or handler, and decide later how it should execute at runtime.

The Core Idea

The library revolves around one main split:

  • IExecutable<TIn, TOut> describes behavior as a reusable typed value,
  • IExecutor<TIn, TOut> runs that behavior with runtime decoration and execution control.

That split is the main reason the library exists. It lets you keep composition stable while changing runtime behavior.

The same executable chain can be executed with validation, fallback, timeout, retry, exception mapping, context initialization, result wrapping, caching, metrics, or middleware pipelines without changing the original composition.

Define behavior once. Run it many ways.

Quick Example

IExecutor<string, Result<int>> parseLength =
  Executable.Create((string text) => text.Trim())
    .Then(text => text.Length)
    .GetExecutor()
    .WithPolicy(policy => policy
      .ValidateInput(text => !string.IsNullOrWhiteSpace(text), "Value is required")
      .ValidateOutput(length => length >= 0, "Length must be non-negative"))
    .WithResult();

Result<int> result = parseLength.Execute("  demo  ");

What happens here:

  • Executable.Create(...) wraps behavior as a reusable executable unit.
  • Then(...) composes the next step.
  • GetExecutor() moves from composition to execution.
  • WithPolicy(...) adds runtime validation.
  • WithResult() turns success and failure into an explicit return contract.

The important part is that the executable composition itself stays reusable.

Reusing The Same Composition

IExecutable<string, int> parse =
  Executable.Create((string text) => text.Trim())
    .Then(text => int.Parse(text));

IExecutor<string, Result<int>> safe =
  parse
    .GetExecutor()
    .WithPolicy(policy => policy
      .ValidateInput(text => !string.IsNullOrWhiteSpace(text), "Value is required")
      .Fallback<FormatException>((_, _) => 0))
    .WithResult();

IExecutor<string, Optional<int>> forgiving =
  parse
    .GetExecutor()
    .SuppressException()
    .OfType<FormatException>();

This is the same composition executed through two different runtime contracts:

  • safe returns Result<int>,
  • forgiving returns Optional<int>.

The logic is the same. Only the runtime changes.

Main Building Blocks

Executables gives you one model that covers reusable executable units, pipeline composition, explicit branching, commands and queries, event-driven flows, executor-level policies, middleware-style runtime pipelines, and aligned sync and async APIs.

Executable Composition

Use Executable.Create(...), Then(...), Compose(...), Fork(...), Merge(...), FlatMap(...), Map(...), and related operators to build reusable behavior.

IExecutable<string, string> pipeline =
  Executable.Create((string text) => text.Trim())
    .Then(text => text.ToUpperInvariant())
    .Then(text => $"Value: {text}");

Runtime Execution and Policies

Convert a composition into an executor and decorate it with runtime behavior such as validation, guards, fallback, timeout, retry, reentrancy prevention, or completion-driven cancellation.

IAsyncExecutor<string, Result<int>> runtime =
  AsyncExecutable.Create(async (string text, CancellationToken token) =>
  {
    await Task.Delay(10, token);
    return int.Parse(text);
  })
  .GetExecutor()
  .WithPolicy(policy => policy
    .ValidateInput(text => !string.IsNullOrWhiteSpace(text), "Value is required")
    .Timeout(TimeSpan.FromSeconds(1)))
  .WithResult();

Queries, Commands, and Events

Expose executable logic through more specific application-oriented contracts when you want clearer intent at the API boundary.

IQuery<string, string> query = pipeline.AsQuery();

ICommand<string> command =
  Executable.Create((string text) => !string.IsNullOrWhiteSpace(text))
    .AsCommand();

var changed = new Event<string>();
changed.Subscribe(value => Console.WriteLine($"Changed: {value}"));
changed.Handle(EventPublisher.Sequential<string>());
changed.Publish("Updated");

Middleware Pipelines

Build runtime execution through middleware-style chains when that shape is more natural than isolated policies.

IExecutor<string, string> executor = Pipeline
  .Use((string text, Func<string, int> next) =>
  {
    string normalized = text.Trim();
    int length = next(normalized);
    return $"Length: {length}";
  })
  .End(Executable.Create((string text) => text.Length));

Sync and Async APIs

The library keeps synchronous and asynchronous APIs structurally parallel, so moving to async does not require learning a different model.

IExecutable<string, int> parse = Executable.Create((string text) => int.Parse(text));

IAsyncExecutable<string, string> asyncPipeline =
  parse.Then(async (int value, CancellationToken token) =>
  {
    await Task.Delay(1, token);
    return $"Value: {value}";
  });

Why Not Just Use Methods, Delegates, or Middleware?

Direct methods are often enough for small, local, straightforward code. Executables becomes useful when behavior needs to be extracted from ad hoc call chains, shared across different application boundaries, composed from reusable steps, executed with different runtime policies, or adapted without rewriting the core logic.

Delegates are a good low-level representation of behavior, but they do not by themselves give you a consistent composition API, reusable runtime decoration, specialized contracts like queries and commands, structured branching, policy pipelines, reversible typed adaptation, or aligned sync and async executable models.

Middleware and resilience libraries solve important parts of the problem, but usually from specific angles. Executables models application behavior itself as typed composable values and then lets you apply runtime control around that behavior. Depending on the use case, these approaches can complement each other.

When to Use It

Executables fits especially well when application behavior should be:

  • explicit,
  • strongly typed,
  • reusable,
  • testable in isolation,
  • adaptable to multiple runtime environments,
  • composed without committing to a heavy framework.

Good fits include:

  • application services,
  • orchestration layers,
  • command and query flows,
  • reusable workflow steps,
  • boundary-heavy code with validation and fallback logic,
  • explicit runtime control around execution.

When Not to Use It

Executables is probably not the right tool when:

  • direct methods already express behavior clearly,
  • the application is very small and simple,
  • composition is not important,
  • runtime decoration is minimal,
  • introducing an explicit abstraction layer would add more complexity than value.

It is also not:

  • a DI container,
  • a distributed messaging system,
  • a reactive stream library,
  • a framework that dictates your entire architecture.

Installation

dotnet add package Executables

Supported target frameworks:

  • net9.0
  • netstandard2.1
  • net48

Documentation

Local Documentation

The repository uses a local DocFX tool manifest.

dotnet tool restore
dotnet docfx docs\docfx.json --serve

Project Files

Product 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.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
.NET Framework net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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
1.0.0 108 4/9/2026

Initial release.