Skip to content

Vladimir-Bosnjak/VoidAdapterLab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Void Adapter for Unified Execution Pipelines

This repository demonstrates a small but practical technique for unifying execution pipelines that need to handle both:

  • methods that return a value(TResult) / Func<TResult>
  • methods that do not return a value (void / Action / Action<T>)

Problem

In real systems (retry policies, logging wrappers, metrics, orchestration layers), you often want a single execution pipeline.

However, you quickly run into a mismatch:

  • Func<TResult> fits nicely into a composable pipeline
  • Action does not

This typically leads to:

  • duplicated logic
  • separate code paths
  • subtle divergence over time

Idea

Instead of branching the pipeline, adapt the void case.

Convert: Action

into: Func<Unit>

by wrapping the call and returning a placeholder value. This allows everything to flow through a single generic path.

Minimal Example

public static class Pipeline
{
    public static TResult Execute<TResult>(Func<TResult> func)
    {
        return func();
    }

    public static void Execute(Action action)
    {
        Execute(() =>
        {
            action();
            return Unit.Value;
        });
    }
}

public readonly struct Unit
{
    public static readonly Unit Value = new();
}

Why this matters

This pattern enables:

  • a single execution pipeline
  • consistent behavior across all call types
  • easier composition (retry, logging, metrics, etc.)
  • reduced duplication

The value is not the Unit type itself, but the ability to treat all operations uniformly.

Where this is useful

  • retry mechanisms
  • resilience policies
  • middleware-style pipelines
  • decorators / cross-cutting concerns
  • async orchestration layers

Notes

  • This is a simple adapter, not a full functional programming abstraction.
  • The naming Unit is conventional, but any placeholder type works.
  • In async scenarios, the same idea applies to:
    • Func<Task>
    • Func<Task<TResult>>

Takeaway

If your pipeline is generic, your inputs should be too.

Adapting void into a value is often the simplest way to get there.

About

Demonstrates how to adapt an Action into a Func<TResult> to enable a single unified execution pipeline.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages