CSharpFunctionalExtensions icon indicating copy to clipboard operation
CSharpFunctionalExtensions copied to clipboard

Usage question; changing the implementation to railway oriented programming

Open stefan-agache opened this issue 5 years ago • 4 comments

Hi, I'm not sure if this is the best place to ask questions, but I've started a project some time ago using the version 1.17 of this nice framework. I've come across a simple problem that I jut can't get my head around. You can check the code below. I'm trying to transform a simple if/else to the Railway approach of writing the code. The problem, from what I can see, is that I cannot make the OnFailureCompensate run only if the failure is coming from the Create method. I've tried implementing an extension of my own; similar to the Tap method that exists in the newer versions of the framework, but I get into an issue regarding generic results.

So here's the code. I need to make the After() method behave just like the Before() method.

namespace ConsoleApp
{
    using System;

    using CSharpFunctionalExtensions;

    public class SomeTest
    {
        public static void Main()
        {
            var instance = new SomeTest();
            Console.WriteLine(instance.Before().IsSuccess);
            Console.WriteLine(instance.After().IsSuccess);

            Console.Read();
        }

        public Result<string> Before()
        {
            var validationResult = Result.Create(() => true, "validation failed");

            if (validationResult.IsFailure)
            {
                return this.B();
            }

            return this.A();
        }

        public Result<string> After()
        {
            return Result.Create(() => true, "validation failed")
                .OnSuccess(() => this.A())
                .OnFailureCompensate(() => this.B());
        }

        public Result<string> A()
        {
            return Result.Fail<string>("A failed");
        }

        public Result<string> B()
        {
            return Result.Ok<string>("B success value");
        }
    }
}

Is there something obvious that I'm missing? I can't seem to find a solution.

stefan-agache avatar Oct 28 '19 05:10 stefan-agache

using new version and according to your flow, you should change After() to:

        public Result<string> After()
        {
            return Result.SuccessIf(() => true, "validation failed")
                .Finally(result => result.IsSuccess ? A() : B());
        }

OnFailureCompensate was intended to switch from fail rail to success rail in your case its acting as compensation for A() failure

golavr avatar Nov 22 '19 10:11 golavr

Hi, thanks for your reply. Using the ternary operator only moves the conditional statement inline and doesn't really help with the clarity of the code. We'll probably use something the likes of

    public static Result<T> OnSuccessIfElse<T>(
        this Result result, 
        Func<Result<T>> successFunc, 
        Func<Result<T>> failFunc)
    {
        if (result.IsSuccess)
        {
            return successFunc();
        }

        return failFunc();
    }

that a colleague of mine suggested and which is really similar to your suggestion and then the After method would look like:

    public Result<string> After()
    {
        return Result.SuccessIf(() => true, "validation failed")
            .OnSuccessIfElse(
                () => this.A(), 
                () => this.B());
    }

I had just hoped that something alike was already in place and that I was just missing it.

stefan-agache avatar Nov 22 '19 10:11 stefan-agache

Just a side note: Result.SuccessIf(() => true, "validation failed") is redundant. The error message is ignored.

The same effect is achieved by calling: Result.Success()

space-alien avatar Nov 22 '19 14:11 space-alien

@space-alien Hi, the issue that I've encountered initially had to do with some code that verified a condition. The issue appeared only when the validation succeeded so I've just replaced the condition with the value "true" for this purpose. If the validation would fail then Before() and After() would have the same result.

If I wanted a successful result I would have used Result.Ok() in version 1.17 or as you've said, Result.Success() in a more recent version.

stefan-agache avatar Nov 22 '19 15:11 stefan-agache