Table of Contents

Struct Unit

Namespace
FunctionalDdd
Assembly
FunctionalDdd.RailwayOrientedProgramming.dll

Represents the absence of a meaningful value, used for operations that have no return value. This is the functional programming equivalent of void, but as a proper type that can be used with generics.

public record struct Unit : IEquatable<Unit>
Implements
Inherited Members
Extension Methods

Examples

Basic usage with Result:

// Operation that doesn't return a value
public Result<Unit> DeleteUser(UserId userId)
{
    var user = _repository.Find(userId);
    if (user == null)
        return Error.NotFound($"User {userId} not found");

    _repository.Delete(user);
    return Result.Success(); // Returns Result<Unit>
}

// Usage
var result = DeleteUser(userId);
if (result.IsSuccess)
    Console.WriteLine("User deleted successfully");

Chaining operations that don't return values:

public async Task<Result<Unit>> ProcessOrderAsync(Order order, CancellationToken ct)
{
    return await ValidateOrder(order)          // Result<Unit>
        .BindAsync(() => ChargePaymentAsync(order, ct))   // Result<Unit>
        .TapAsync(() => SendConfirmationEmailAsync(order.Email, ct))
        .TapAsync(() => UpdateInventoryAsync(order.Items, ct));
}

Converting to HTTP response:

app.MapDelete("/users/{id}", (Guid id, IUserService userService) =>
    UserId.TryCreate(id)
        .Bind(userService.DeleteUser)
        .ToHttpResult());
// Returns 204 No Content on success, appropriate error status on failure

Combining Unit results with value results:

public Result<User> CreateAndNotify(CreateUserRequest request)
{
    return FirstName.TryCreate(request.FirstName)
        .Combine(LastName.TryCreate(request.LastName))
        .Bind((first, last) => User.Create(first, last))
        .Combine(ValidateBusinessRules(request))  // Result<Unit> - just validates
        .Map((user, _) => user);  // Discard Unit, keep User
}

Remarks

In functional programming, every function returns a value. When an operation doesn't have a meaningful return value (like void methods in imperative programming), we use Unit as a placeholder type. This allows Result<TValue> to represent both operations that return values and operations that only succeed or fail without returning data.

Common use cases for Unit:

  • DELETE operations that either succeed or fail but don't return data
  • State-changing operations (e.g., sending emails, updating records)
  • Validation operations that only need to indicate success/failure
  • Fire-and-forget side effects in a functional pipeline

The Result class provides convenience methods for working with Unit:

When converting Result<Unit> to HTTP responses, successful results automatically map to HTTP 204 No Content, indicating success without a response body.

See Also

Result
Result<TValue>