ResultR v1.0.2 Released: Functional Pattern Matching with Match()

ResultR v1.0.2 is now available. This release introduces a new Match() function that makes working with results feel more functional and expressive—especially in API/controller code where you commonly branch on success vs. failure.

What’s New in v1.0.2

Pattern Matching with Match()

The new Match() method provides a clean, functional approach to handling results by executing one of two callbacks depending on whether the result is a success or failure.

Instead of manually checking flags or branching with if/else, you can return the outcome directly:

[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
    var result = await _dispatcher.Dispatch(new GetUserRequest(id));
    
    return result.Match(
        user => Ok(user),
        error => NotFound(new { error })
    );
}

[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserRequest request)
{
    var result = await _dispatcher.Dispatch(request);
    
    return result.Match(
        user => CreatedAtAction(nameof(GetUser), new { id = user.Id }, user),
        error => BadRequest(new { error })
    );
}

Why this matters:

  • Keeps “happy path” and “error path” in one obvious place
  • Encourages a more declarative style
  • Reduces boilerplate branching code in handlers/controllers

Accessing Exception Information

When you need more detail on failures, use the overload that provides both the error message and the exception. This is especially useful for logging while still returning a clean response.

[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
    var result = await _dispatcher.Dispatch(new GetUserRequest(id));
    
    return result.Match(
        user => Ok(user),
        (error, exception) => 
        {
            if (exception is not null)
            {
                _logger.LogError(exception, "Failed to get user {UserId}", id);
            }
            return NotFound(new { error, hasException = exception is not null });
        }
    );
}

Match() for Non-Generic Result

Match() is also available on the non-generic Result type—perfect for “command-style” operations where success doesn’t return a value.

public async Task<IActionResult> DeleteUser(int id)
{
    var result = await _dispatcher.Dispatch(new DeleteUserRequest(id));
    
    return result.Match(
        () => NoContent(),
        error => NotFound(new { error })
    );
}

// With exception handling
public async Task<IActionResult> DeleteUser(int id)
{
    var result = await _dispatcher.Dispatch(new DeleteUserRequest(id));
    
    return result.Match(
        () => NoContent(),
        (error, exception) => 
        {
            _logger.LogError(exception, "Failed to delete user {UserId}", id);
            return StatusCode(500, new { error });
        }
    );
}

Try v1.0.2

If you’re already using ResultR, upgrading to v1.0.2 gives you a more fluent way to translate results into HTTP responses (or any other branching logic) without sacrificing clarity.

Feedback Welcome

If you try out Match() and have ideas for additional ergonomic improvements (more overloads, async variants, etc.), open an issue or start a discussion on GitHub—feedback helps guide the next releases.


Built with ❤️ for the C# / DotNet community.

Avatar
Alan P. Barber
Software Developer, Computer Scientist, Scrum Master, & Crohn’s Disease Fighter

I specialize in Software Development with a focus on Architecture and Design.

comments powered by Disqus
Next
Previous

Related