### Starting a Chain with ResultBox.Start Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Illustrates using `ResultBox.Start` as a neutral starting point for operation chains, particularly useful before combining multiple operations. It creates a successful ResultBox with no specific value, allowing subsequent operations to add the first real data. ```csharp // Sometimes you need a neutral starting point, especially before Combine. ResultBox start = ResultBox.Start; // A successful box with no specific value. ResultBox result = start .Combine(GetUserName) // Now combine can add the first real value .Remap(combined => combined.Value1); // Extract the username // ResultBox.Start is often used implicitly when the first step doesn't need input. ``` -------------------------------- ### Install ResultBoxes Package Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Installs the ResultBoxes NuGet package using the .NET CLI. This command adds the library as a dependency to your C# project, enabling its use for Railway Oriented Programming. ```sh dotnet add package ResultBoxes ``` -------------------------------- ### ResultBox Handling Examples Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Demonstrates various methods for handling ResultBox instances in C#, including switch expressions, if statements, and ternary operators. Explains how to access success values and exceptions safely, and the reasoning behind the design. ```csharp internal class Program { public static ResultBox Increment(int target) => target switch { > 1000 => new ArgumentOutOfRangeException(nameof(target)), _ => target + 1 }; private static void Main(string[] args) { // Handle ResultBox with switch expression // Value: 101 var result = Increment(100); switch (result) { case { IsSuccess: false }: Console.WriteLine($"Error: {result.GetException().Message}"); break; case { IsSuccess: true }: Console.WriteLine($"Value: {result.GetValue()}"); break; } // Log() displays value.ToString() if IsSuccess is true, otherwise displays exception.Message // case2 Error: Specified argument was out of the range of valid values. (Parameter 'target') Increment(1001).Log("case2"); // RunIncrement() is a method that handles ResultBox with switch expression // Value: 101 Console.WriteLine(RunIncrement(100)); // Handle ResultBox with if statement // Error: Specified argument was out of the range of valid values. (Parameter 'target') var result4 = Increment(1001); if (result4.IsSuccess) { Console.WriteLine($"Value: {result4.GetValue()}"); } else { Console.WriteLine($"Error: {result4.GetException().Message}"); } // Handle ResultBox with ternary operator ?: // Value: 2 var result5 = Increment(1); Console.WriteLine( result5.IsSuccess ? $\"Value: {result5.GetValue()}\" : $\"Error: {result5.GetException().Message}\"); } // Handle ResultBox with switch expression private static string RunIncrement(int target) => Increment(target) switch { { IsSuccess: false } error => $\"Error: {error.GetException().Message}\", { IsSuccess: true } success => $\"Value: {success.GetValue()}\" }; } ``` -------------------------------- ### C# Railway Oriented Programming with Try/Catch Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Demonstrates Railway Oriented Programming (ROP) in C# using `ResultBox` and `WrapTry`. This pattern allows chaining operations that can either succeed or fail, gracefully handling functions that throw exceptions by converting them into `ResultBox` errors. The examples cover successful execution and different failure conditions. ```csharp internal class Program { public static ResultBox Increment(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Increment)}"), _ => target + 1 }; public static int IncrementWithThrowing(int target) => target switch { > 1000 => throw new ApplicationException( $"{target} is not allowed for {nameof(Increment)}"), _ => target + 1 }; public static ResultBox Double(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Double)}"), _ => target * 2 }; public static ResultBox Triple(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Triple)}"), _ => target * 3 }; public static int TripleWithThrowing(int target) => target switch { > 1000 => throw new ApplicationException($"{target} is not allowed for {nameof(Triple)}"), _ => target * 3 }; private static void Main(string[] args) { // IncrementWithThrowing and TripleWithThrowing can throw exceptions // WrapTry is used to catch exceptions and return them as error // Calculate (1 + 1) * 2 * 3 = 12 // Value: 12 ResultBox.WrapTry(() => IncrementWithThrowing(1)) .Conveyor(Double) .ConveyorWrapTry(TripleWithThrowing) .Log(); // IncrementWithThrowing and TripleWithThrowing can throw exceptions // WrapTry is used to catch exceptions and return them as error // Error: 2000 is not allowed for Increment ResultBox.WrapTry(() => IncrementWithThrowing(2000)) .Conveyor(Double) .ConveyorWrapTry(TripleWithThrowing) .Log(); // IncrementWithThrowing and TripleWithThrowing can throw exceptions // WrapTry is used to catch exceptions and return them as error // Error: 1001 is not allowed for Double ResultBox.WrapTry(() => IncrementWithThrowing(1000)) .Conveyor(Double) .ConveyorWrapTry(TripleWithThrowing) .Log(); // IncrementWithThrowing and TripleWithThrowing can throw exceptions // WrapTry is used to catch exceptions and return them as error // Error: 1202 is not allowed for Triple ResultBox.WrapTry(() => IncrementWithThrowing(600)) .Conveyor(Double) .ConveyorWrapTry(TripleWithThrowing) .Log(); } } ``` -------------------------------- ### CombineValue and Conveyor Method Chaining in C# Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Demonstrates chaining operations using ResultBox's `Combine` and `Conveyor` methods in C#. This pattern allows passing multiple values between operations and gracefully handling errors, illustrated with examples of successful calculations and error scenarios. ```csharp internal class Program { public static ResultBox Increment(int target) => target switch { > 1000 => new ApplicationException( $"{target} can not use for the {nameof(Increment)}. It should be under or equal 1000"), _ => target + 1 }; public static ResultBox Add(int target1, int target2) => target1 switch { > 100 => new ApplicationException($"over 100 is not allowed for {nameof(Add)}"), _ => target1 + target2 }; public static ResultBox Divide(int numerator, int denominator) => (numerator, denominator) switch { (_, 0) => new ApplicationException("can not divide by 0"), _ => numerator / denominator }; private static void Main(string[] args) { // Pattern 1 : Use Combine method chain // calculate answer = (29 + 1) / (1 + 9) = 3 // Value: 3 Increment(29) .Combine(Add(1, 9)) .Conveyor(Divide) .Log(); // Pattern 2 : Error in Increment method (target > 1000) // Exception3: 2000 can not use for the Increment. It should be under or equal 1000 Increment(2000) .Combine(Add(1, 9)) .Conveyor(Divide) .Log(); // Pattern 4 : Error in Add method (target1 > 100) // Exception4: over 100 is not allowed for Add Increment(19) .Combine(Add(1000, 9)) .Conveyor(Divide) .Log(); // Pattern 5 : Error in Divide method (denominator <> 0) // Exception5: can not divide by 0 Increment(19) .Combine(Add(0, 0)) .Conveyor(Divide) .Log(); } } ``` -------------------------------- ### Mixing Async and Sync ResultBox Chaining in C# Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Illustrates chaining both asynchronous (Task>) and synchronous (ResultBox) functions using Conveyor methods. This example highlights the flexibility of the ResultBox pattern in handling mixed-mode operations. ```csharp internal class Program { public static ResultBox Increment(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Increment)}"), _ => target + 1 }; public static ResultBox Double(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Double)}"), _ => target * 2 }; public static ResultBox Triple(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Triple)}"), _ => target * 3 }; public static Task> IncrementAsync(int target) => Task.FromResult>( target switch { > 1000 => new ApplicationException( $"{target} is not allowed for {nameof(IncrementAsync)}"), _ => target + 1 }); public static Task> DoubleAsync(int target) => Task.FromResult>( target switch { > 1000 => new ApplicationException( $"{target} is not allowed for {nameof(DoubleAsync)}"), _ => target * 2 }); public static Task> TripleAsync(int target) => Task.FromResult>( target switch { > 1000 => new ApplicationException( $"{target} is not allowed for {nameof(TripleAsync)}"), _ => target * 3 }); private static async Task Main(string[] args) { // Error: System.ApplicationException: 1001 is not allowed for IncrementAsync await Increment(1001) .Conveyor(DoubleAsync) .Conveyor(TripleAsync) .Log(); // Error: System.ApplicationException: 1001 is not allowed for DoubleAsync await IncrementAsync(1000) .Conveyor(Double) .Conveyor(TripleAsync) .Log(); // Error: System.ApplicationException: 1202 is not allowed for TripleAsync await IncrementAsync(600) .Conveyor(DoubleAsync) .Conveyor(Triple) .Log(); // Value: 24 await IncrementAsync(3) .Conveyor(DoubleAsync) .Conveyor(TripleAsync) .Log(); } } ``` -------------------------------- ### Handle Nullable Values with OptionalValue in C# Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Demonstrates how to use OptionalValue within ResultBox to manage cases where a value might be absent or empty, preventing the misuse of null for both values and exceptions. This example shows converting a string to a half-length string, returning either a value, an empty state, or an exception. ```csharp internal class Program { public static ResultBox> ConvertStringToHalfLength(string input) => input.Length switch { 0 => new ApplicationException("Input string is empty"), // Exception 1 => OptionalValue.Empty, // Not error but Empty _ => OptionalValue.FromValue(input[..^(input.Length / 2)]) // has value }; private static void Main(string[] args) { // Error: Input string is empty ConvertStringToHalfLength("").Log(); // Value: OptionalValue { Value = , HasValue = False } ConvertStringToHalfLength("H").Log(); // Value: OptionalValue { Value = Hel, HasValue = True } ConvertStringToHalfLength("Hello").Log(); } } ``` -------------------------------- ### Chaining Operations with Conveyor Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Demonstrates how to chain operations sequentially using the Conveyor method. Each step in the chain only executes if the previous step resulted in a success. This is ideal for pipelines where subsequent operations depend on the successful completion of prior ones. ```csharp // Functions returning ResultBox ResultBox ParseNumber(string text) { /* ... returns Success(number) or Failure(error) ... */ } ResultBox Increment(int number) { /* ... returns Success(number + 1) or Failure(error) ... */ } ResultBox FormatNumber(int number) { /* ... returns Success($"Value: {number}") or Failure(error) ... */ } // --- Usage --- string inputText = "10"; ResultBox finalResult = ResultBox.FromValue(inputText) // Start with a Success box .Conveyor(ParseNumber) // If success, parse text to number. If failure, skip next. .Conveyor(Increment) // If success, increment number. If failure, skip next. .Conveyor(FormatNumber); // If success, format number to string. // finalResult will contain either Success("Value: 11") or a Failure from any step. ``` -------------------------------- ### Validating Intermediate Results with Verify Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Shows how to use the `Verify` method to ensure intermediate results meet specific criteria. If the validation fails, it returns a `Failure` with a provided exception; otherwise, it passes the result through. ```csharp ResultBox GetUserInputNumber() { /* ... */ } // --- Usage --- // Ensure the number is positive after getting it. ResultBox validatedResult = GetUserInputNumber() .Verify(number => { if (number > 0) { return ExceptionOrNone.None; // Validation passed } else { return new ArgumentOutOfRangeException("Number must be positive"); // Validation failed } }); // If GetUserInputNumber succeeded but the number wasn't positive, // validatedResult becomes Failure(ArgumentOutOfRangeException). // Otherwise, it keeps the original Success or Failure from GetUserInputNumber. ``` -------------------------------- ### Handling Failures with Rescue Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Demonstrates how to recover from a failure within a ResultBox chain using the Rescue method. This allows you to provide alternative logic or default values when a specific error occurs, without stopping the chain if the error is handled. ```csharp ResultBox GetPrimarySetting() { /* ... might return Failure("Primary not found") ... */ } string GetDefaultSetting() { return "DefaultValue"; } // --- Usage --- ResultBox settingResult = GetPrimarySetting() .Rescue(error => { // Check if it's the specific error we can handle if (error.Message == "Primary not found") { // Recover by returning a default value return ResultBox.FromValue(GetDefaultSetting()); } else { // Can't handle other errors, keep it as Failure return error; } }); // settingResult will be Success("DefaultValue") if the primary failed but was rescued, // or the original Success/Failure otherwise. ``` -------------------------------- ### Async Task Functions with ResultBox Chaining in C# Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Demonstrates using Task> with async chaining methods like Conveyor and Log in C#. It showcases how to handle potential exceptions within asynchronous operations using the ResultBox pattern. ```csharp internal class Program { public static Task> IncrementAsync(int target) => Task.FromResult>( target switch { > 1000 => new ApplicationException( $"{target} is not allowed for {nameof(IncrementAsync)}"), _ => target + 1 }); public static Task> DoubleAsync(int target) => Task.FromResult>( target switch { > 1000 => new ApplicationException( $"{target} is not allowed for {nameof(DoubleAsync)}"), _ => target * 2 }); public static Task> TripleAsync(int target) => Task.FromResult>( target switch { > 1000 => new ApplicationException( $"{target} is not allowed for {nameof(TripleAsync)}"), _ => target * 3 }); private static async Task Main(string[] args) { // Error: System.ApplicationException: 1001 is not allowed for IncrementAsync await IncrementAsync(1001) .Conveyor(DoubleAsync) .Conveyor(TripleAsync) .Log(); // Error: System.ApplicationException: 1001 is not allowed for DoubleAsync await IncrementAsync(1000) .Conveyor(DoubleAsync) .Conveyor(TripleAsync) .Log(); // Error: System.ApplicationException: 1202 is not allowed for TripleAsync await IncrementAsync(600) .Conveyor(DoubleAsync) .Conveyor(TripleAsync) .Log(); // Value: 24 await IncrementAsync(3) .Conveyor(DoubleAsync) .Conveyor(TripleAsync) .Log(); } } ``` -------------------------------- ### Combining Multiple Results with Combine Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Shows how to combine the results of multiple operations into a single ResultBox, typically holding a tuple or custom structure of the successful values. The Combine method handles both synchronous and asynchronous operations seamlessly, executing them only if preceding steps were successful. ```csharp ResultBox GetUserName() { /* ... returns Success(name) or Failure(error) ... */ } ResultBox GetUserEmail() { /* ... returns Success(email) or Failure(error) ... */ } // --- Usage --- // Combine needs a starting point, often ResultBox.Start (a neutral success) ResultBox> combinedResult = ResultBox.Start .Combine(GetUserName) // Get the first value .Combine(GetUserEmail); // Get the second value // combinedResult contains Success(TwoValues("Alice", "alice@example.com")) // OR Failure if either GetUserName or GetUserEmail failed. // You can then Remap the TwoValues if needed. // Async example: async Task> GetEmailAsync() { /* ... */ } ResultBox> asyncCombined = await ResultBox.Start .Combine(GetUserName) // Sync operation .Combine(GetEmailAsync); // Async operation - Combine handles the mix ``` -------------------------------- ### Exiting the Box Safely with UnwrapBox Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Explains the `UnwrapBox` method for extracting values from a ResultBox, intended for situations where success is expected and failure is considered exceptional. It throws an exception on failure, breaking the ROP flow. ```csharp ResultBox GetCriticalData() { /* ... should succeed normally ... */ } // --- Usage --- // Use UnwrapBox when you expect success and consider failure exceptional (will throw). // This is often done outside the main ROP chain, e.g., in the final presentation layer. try { string criticalValue = GetCriticalData().UnwrapBox(); Console.WriteLine($"Critical data: {criticalValue}"); } catch (Exception ex) { // If GetCriticalData returned Failure, the exception is thrown here. Console.WriteLine($"Failed to get critical data: {ex.Message}"); // Handle the thrown exception appropriately. } // You can also provide a function to UnwrapBox to transform the value on success: // int length = GetCriticalData().UnwrapBox(s => s.Length); // Throws if GetCriticalData failed. // *Warning:* `UnwrapBox` breaks the ROP flow by throwing exceptions on failure. Prefer `.Match()` for safely handling both outcomes within the flow. ``` -------------------------------- ### Transforming Values with Remap Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Illustrates how to transform the successful value within a ResultBox using the Remap method. The provided transformation function is only executed if the ResultBox is in a success state, otherwise, the failure is propagated. ```csharp ResultBox GetUserId() { /* ... returns Success(userId) or Failure(error) ... */ } // --- Usage --- ResultBox result = GetUserId() .Remap(id => $"User ID is {id}"); // Only runs if GetUserId succeeded. // result will contain Success("User ID is 123") or the Failure from GetUserId. ``` -------------------------------- ### Wrap Try-Catch Blocks with ResultBox.WrapTry in C# Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Illustrates how to use the `WrapTry` method in ResultBox to convert functions that might throw exceptions into a ResultBox that either contains the successful return value or the caught exception. This simplifies error handling by treating exceptions as part of the return type. ```csharp internal class Program { public static int Divide(int numerator, int denominator) => denominator == 0 ? throw new ApplicationException("can not divide by 0") : numerator / denominator; private static void Main(string[] args) { // This will return exception result // Error: can not divide by 0 ResultBox.WrapTry(() => Divide(10, 0)).Log(); // This will return value result // Value: 5 ResultBox.WrapTry(() => Divide(10, 2)).Log(); } } ``` -------------------------------- ### Transforming Errors with RemapException Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Demonstrates how to convert specific low-level errors into more meaningful custom exceptions within a ResultBox chain. This method takes a function that maps an exception to another exception, allowing for tailored error handling. ```csharp ResultBox RiskyOperation() { /* ... might return Failure(new IOException(...)) ... */ } // --- Usage --- // Sometimes you want to convert a specific low-level error into a more meaningful one. ResultBox result = RiskyOperation() .RemapException(error => { if (error is IOException) { // Convert IOException to a custom ApplicationException return new ApplicationException("Failed due to I/O problem", error); } else { // Leave other errors unchanged return error; } }); // If RiskyOperation failed with IOException, result is now Failure(ApplicationException). // Other failures or successes from RiskyOperation remain unchanged. ``` -------------------------------- ### Wrap Void Functions with ResultBox.WrapTry in C# Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Shows how to wrap C# void functions using `ResultBox.WrapTry`. Since `ResultBox` is not possible, the library uses a `UnitValue` type to represent the successful completion of a void operation. This allows consistent error handling for functions with and without return values. ```csharp internal class Program { public static int Divide(int numerator, int denominator) => denominator == 0 ? throw new ApplicationException("can not divide by 0") : numerator / denominator; private static void Main(string[] args) { // This will return exception result // Error: can not divide by 0 ResultBox.WrapTry(() => Divide(10, 0)).Log(); // This will return value result // Value: 5 ResultBox.WrapTry(() => Divide(10, 2)).Log(); } } ``` -------------------------------- ### ROP with ResultBox Conveyor Source: https://github.com/j-tech-japan/resultboxes/blob/main/README.md Illustrates the Railway Oriented Programming (ROP) pattern using ResultBox and its Conveyor method. This pattern allows chaining functions, where execution proceeds only if the previous step was successful. If an error occurs at any step, subsequent functions are skipped, and the error is propagated. ```csharp internal class Program { public static ResultBox Increment(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Increment)}"), _ => target + 1 }; public static ResultBox Double(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Double)}"), _ => target * 2 }; public static ResultBox Triple(int target) => target switch { > 1000 => new ApplicationException($"{target} is not allowed for {nameof(Triple)}"), _ => target * 3 }; private static void Main(string[] args) { // Error: System.ApplicationException: 1001 is not allowed for Increment Increment(1001) .Conveyor(Double) .Conveyor(Triple) .Log(); // Error: System.ApplicationException: 1001 is not allowed for Double Increment(1000) .Conveyor(Double) .Conveyor(Triple) .Log(); // Error: System.ApplicationException: 1202 is not allowed for Triple Increment(600) .Conveyor(Double) .Conveyor(Triple) .Log(); // Value: 24 Increment(3) .Conveyor(Double) .Conveyor(Triple) .Log(); } } ``` -------------------------------- ### Checking for Nulls with CheckNull Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Provides a safe way to handle potentially null return values from methods. The CheckNull static method converts a null return into a Failure ResultBox, optionally with a custom exception, ensuring that subsequent operations only proceed with valid, non-null values. ```csharp string? GetOptionalValue() { /* ... might return null ... */ } // --- Usage --- ResultBox checkedResult = ResultBox.CheckNull(GetOptionalValue(), new ApplicationException("Value was missing!")); // checkedResult is Success(value) if GetOptionalValue() returned non-null, // otherwise it's Failure(ApplicationException("Value was missing!")). // If no custom exception is given, it defaults to ResultValueNullException. ``` -------------------------------- ### Performing Side Effects with Do Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Allows executing a side-effecting action (like logging or updating UI) when a ResultBox is in a success state, without altering the ResultBox's value or state. The Do method takes an action that receives the successful value, and the chain continues unaffected. ```csharp ResultBox ProcessData(int data) { /* ... */ } void LogValue(int value) { Console.WriteLine($"Logging successful value: {value}"); } // --- Usage --- ResultBox result = ResultBox.FromValue(10) .Do(LogValue) // If Success, calls LogValue(10). Doesn't change the box. .Conveyor(ProcessData); // Continues the chain // LogValue is only called if the box entering .Do is Success. ``` -------------------------------- ### Safe Casting with Cast Source: https://github.com/j-tech-japan/resultboxes/blob/main/ResultBox_LLM.md Enables safe type casting within a ResultBox chain. The Cast method attempts to cast the successful value to a specified type. If the cast fails, it results in a Failure ResultBox, preventing runtime exceptions and propagating the error. ```csharp interface IAnimal { } record Dog : IAnimal { public void Bark() { /* ... */ } } record Cat : IAnimal { } ResultBox GetAnimal() { /* ... returns Success(new Dog()) or Success(new Cat()) or Failure ... */ } // --- Usage --- ResultBox dogResult = GetAnimal() .Cast(); // Attempts to cast the IAnimal to a Dog // If GetAnimal returned Success(Dog), dogResult is Success(Dog). // If GetAnimal returned Success(Cat) or Failure, dogResult is Failure (InvalidCastException or original error). ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.