question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Question: Could you suggest how to rewrite this more functional and using more benefits of this library

See original GitHub issue

Hi, Could you suggest how to rewrite this more functional and using more benefits of this library Thanks

using System;
using System.Diagnostics.Contracts;
using System.Threading.Tasks;
using LanguageExt;
using LanguageExt.Common;

namespace LanguageExtQuestions
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var req = new Request { Name = "SOMENAME" };

            var result = ValidateRequestForErrors(req)
                .BindAsync(val => CreateEnvironment(val)).Result;
            result.Match(
                id =>
                {
                    // LOG
                },
                errors =>
                {
                    // LOG
                });
        }

        private static Validation<Error, ValidatedRequest> ValidateRequestForErrors(Request req)
        {
            // This can produce multiple validation errors
            return new ValidatedRequest { Name = req.Name };
        }

        public static async Task<Validation<Error, string>> CreateEnvironment(ValidatedRequest req)
        {
            var validated = ValidateCreateEnvironmentRequest(req);

            var freeEnvironment = validated.BindAsync(async req =>
                await GetNewEnvironment());

            return await freeEnvironment.BindT(TryCallPipeline);

            async Task<Validation< Error, string>> TryCallPipeline(string environment)
            {
                var result = await CallPipeline(environment, req);

                result = await result.BindAsync<Error, string, string>(
                    async env =>
                    {
                        await UpdateEnvironment(env);
                        return env;
                    });
                result.IfFail(_ => ReleaseEnvironmentDueToError(environment));
                return result;
            }
        }

        private static Validation<Error, ValidatedRequest> ValidateCreateEnvironmentRequest(ValidatedRequest req)
        {
            if (string.IsNullOrWhiteSpace(req.Name))
            {
                // LOG ERROR
                return (Error)"Name is not provided";
            }
            return req;
        }

        private static Task<Validation<Error, string>> GetNewEnvironment()
        {
            // LOG MESSAGE
            // Some more complex logic that need to be awaited, but can result in Error
            return Prelude.Success<Error, string>("FREE_ENVIRONMENT").AsTask(); // CALL API
        }

        private static async Task<Validation<Error, string>> CallPipeline(string environment, ValidatedRequest req)
        {
            // LOG MESSAGE
            // Some more complex logic using HttpClien that need to be awaited, but can result in Error
            return await Prelude.Success<Error, string>("FREE_ENVIRONMENT").AsTask(); // CALL API
        }

        private static async Task UpdateEnvironment(string environment)
        {
            // LOG MESSAGE
            // Some more complex logic that need to be awaited, can result in Error but it is ignored for now
            await Task.Delay(1); // CALL API
        }

        private static void ReleaseEnvironmentDueToError(string environment)
        {
            // LOG MESSAGE
            // CALL RELEASE API
        }
    }

    public class Request
    {
        public string Name { get; set; }
    }

    public class ValidatedRequest
    {
        public string Name { get; set; }
    }

    public static class Exts
    {
        [Pure]
        public static async Task<Validation<TFail, T2>> BindAsync<TFail, T1, T2>(this Validation<TFail, T1> r, Func<T1, Task<Validation<TFail, T2>>> f) =>
            await r.AsTask().BindT(f);
    }
}

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:11 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
bmazzarolcommented, Nov 16, 2022

Just to clarify, coalesce on validation will only be Success if ALL validations coalesced are successful. Order is not important.

[Fact]
public static void Test4()
{
    var error1 = Fail<Error, string>(Error.New("error 1"));
    var pass = Success<Error, string>("pass 1");
    var error2 = Fail<Error, string>(Error.New("error 2"));

    // having pass first is not going to make it a Success
    var result =pass | error1 | error2;

    Assert.True(result.IsFail);
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 1");
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 2");
}

[Fact]
public static void Test5()
{
    var pass = Success<Error, string>("pass 1");

    var result =pass | pass | pass;

    Assert.True(result.IsSuccess);
}
1reaction
bmazzarolcommented, Nov 16, 2022

Good catch @BrunodaSilvaBelo!

You need to use the Apply that matches the number of elements in the tuple.

The function it uses internally is actually very powerful and works well for n number of validations on the SAME type/instance.

[Fact]
public static void Test()
{
    var error1 = Fail<Error, string>(Error.New("error 1"));
    var pass = Success<Error, string>("pass 1");
    var error2 = Fail<Error, string>(Error.New("error 2"));

    var result = error1.Disjunction(pass).Disjunction(error2);
    
    Assert.True(result.IsFail);
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 1");
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 2");
}

Another way to do this is with Sequence followed by a Map/Select to the head of the list

[Fact]
public static void Test2()
{
    var error1 = Fail<Error, string>(Error.New("error 1"));
    var pass = Success<Error, string>("pass 1");
    var error2 = Fail<Error, string>(Error.New("error 2"));

    var result = Seq(error1,pass,error2).Sequence().Select(x => x.Head);

    Assert.True(result.IsFail);
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 1");
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 2");
}

The last way (I’m sure there are more ways…going to stop at three) is to use the coalesce operator defined on Validation


[Fact]
public static void Test3()
{
    var error1 = Fail<Error, string>(Error.New("error 1"));
    var pass = Success<Error, string>("pass 1");
    var error2 = Fail<Error, string>(Error.New("error 2"));

    var result = error1 | pass | error2;

    Assert.True(result.IsFail);
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 1");
    Assert.Contains(result.FailToSeq(), x => x.Message == "error 2");
}

I think the last one is cool so have updated my other example to use that. 😄

Read more comments on GitHub >

github_iconTop Results From Across the Web

Interview Question: "How Do You Adapt to Change?"
In this article, we explain why employers ask how you adapt to change and we offer advice on ways to answer this interview...
Read more >
What is ChatGPT, DALL-E, and generative AI?
Generative artificial intelligence (AI) describes algorithms (such as ChatGPT) that can be used to create new content, including audio, ...
Read more >
8 things you didn't know you could do with GitHub Copilot
First, I recommend writing clear, understandable comments to help your AI pair programmer generate desired solutions, but if you're interested ...
Read more >
How to Deal with Resistance to Change
The steps include emphasizing new standards of performance for staff specialists and encouraging them to think in different ways, as well as making...
Read more >
Submission Guidelines | PLOS ONE
We recommend that you cite supporting information in the manuscript text, but this is not a requirement. If you cite supporting information in...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found