Skip to content

Add expected-error ProcessResult variant - #10

Merged
aaron-salisbury merged 6 commits into
mainfrom
process-result-expected-errors
Sep 19, 2026
Merged

aaron-salisbury merged 6 commits into
mainfrom
process-result-expected-errors

Conversation

@aaron-salisbury

Copy link
Copy Markdown
Member

Summary

Adds a new ProcessResult<T, TError> variant for operations that can fail in expected, explicitly modeled ways, while keeping the existing ProcessResult<T> focused on value-or-exception scenarios.

This change is intentionally small and follows the same distinction made by dotNext's result types: expected application/domain outcomes and exceptional failures are different concepts and should not be forced through the same error channel.

When to use each result type

ProcessResult<T>

Use ProcessResult<T> when an operation intentionally captures an exception as part of its result contract.

Typical examples include boundary code that needs to inspect, forward, log, or defer an exception rather than immediately allowing it to propagate.

Expected application or domain outcomes should not be converted into exceptions merely to fit this type. Unexpected or exceptional failures should normally continue to propagate as exceptions unless a concrete boundary has a reason to capture them.

ProcessResult<T, TError>

Use ProcessResult<T, TError> when failure is an anticipated part of normal application or domain flow and callers are expected to branch on a known error state.

Examples include:

  • a requested resource not being found,
  • a destination no longer existing,
  • a known business-rule outcome,
  • another explicitly modeled non-exceptional failure state.

TError is constrained to an enum. Its default value is reserved to represent success, so error enums should use non-default values for actual failures. A neutral member such as None = 0 may be defined when that improves readability.

This type is intentionally not an exception container. Exceptional failures should still throw unless the calling boundary deliberately chooses exception-capturing semantics via ProcessResult<T>.

API

ProcessResult<T, TError> provides:

  • successful and failed constructors,
  • Success(...) and Failure(...) factories,
  • Value,
  • ValueOrDefault,
  • Error,
  • IsSuccessful,
  • TryGet(...),
  • boolean success conversion,
  • explicit value conversion,
  • validation preventing the default TError value from being used as a failure,
  • XML documentation describing the intended semantics.

Attempting to read Value from an unsuccessful expected-error result throws InvalidOperationException, because the caller is attempting to consume a value that the result does not contain.

Existing ProcessResult<T> documentation

The XML documentation for the existing exception-based result type has been expanded to make the distinction between expected failures and captured exceptions explicit. No existing ProcessResult<T> behavior is changed.

Motivation

The immediate use case came from Helm's Goal/Objective application services. Operations such as moving a Goal can legitimately fail because the Goal or destination LifeDomain no longer exists. Those are expected application outcomes, not exceptional runtime failures.

Rather than returning an ambiguous false, inventing a Helm-specific result abstraction, or wrapping ordinary not-found states in exceptions, AppToolkit can provide a reusable expected-error result type alongside its existing exception result type.

This keeps the two responsibilities distinct:

ProcessResult<T>
    value | captured exception

ProcessResult<T, TError>
    value | expected modeled error

The goal is not to reproduce dotNext's broader monadic API. This adds only the result semantics currently justified by real application use, with future additions driven by actual consumers.

@aaron-salisbury
aaron-salisbury merged commit 0816e8c into main Sep 19, 2026
1 check passed
@aaron-salisbury
aaron-salisbury deleted the process-result-expected-errors branch September 19, 2026 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant