2

I have a function that returns an Either. On finding a Left return I'd like to substitute in some default object and process that. What's the appropriate pattern, using Language Ext, for doing so?

There's a model for this below - myfunc returns an Either, and if it returns a TestFail I'd like to substitute in an empty string[] and process that. The following is ugly -- it checks null and relies on implicit conversion of Either to EitherData by let:

using System.Linq;
using LanguageExt;

// Call with true or false to explore each branch ...
public void Sandbox(bool succeed)
{
    Either<TestFail, string[]> myFunc(bool shouldSucceed)
    {
        if (shouldSucceed)
        {
            return new[] {"hello", "world"};
        }
        return new TestFail("i was doomed.");
    }

    var x =
        from response in myFunc(succeed)
        let failures = response.Left
        let strings = response.Right
        from str in failures != null
            ? new string[0] // if myFunc failed, just process nothing/null obj
            : strings
        select "x" + str;
}

with a simple TestFail class:

public class TestFail
{
    public string Reason { get; }
    public TestFail(string reason)
    {
        Reason = reason;
    }
}

Two things: 1. the implicit cast of a single Either to IEnumerable exposes Left and Right directly without all the safeties of the Either -- is that intentional. 2. what's the nicer way of handling a failure from myfunc?

The smell is that I'm not interested in an Either from myfunc. If I have control of it, I should probably just swallow problems within it and make sure I return the default on failure. If I don't have control of it, however - what to do?

Tim Barrass
  • 4,813
  • 2
  • 29
  • 55
  • Take a look at TryParse(). It returns a bool and the parsed value, using out parameters for one of the two. – Christopher Oct 29 '19 at 16:15
  • 1
    You should use [Match](https://louthy.github.io/language-ext/LanguageExt.Core/LanguageExt/Either_L_R.htm#Match%3CRet%3E) to match by type and return something, eg `MyFunc(..).Match(Left: failures=>new string[0],Right:response.Right)` Note that this library and especially this class conflicts with C# 7's and C# 8's pattern matching expressions. – Panagiotis Kanavos Oct 29 '19 at 16:37
  • For example, you could use an `interface IResult {}` and `Success:IResult`, `Failure:IResult` types allowing you to write eg `result switch { case Success s=>..,case Failure f=>...`. Right now the only problem is that the compiler doesn't recognize that all `IResult` cases are covered and demands a default clause – Panagiotis Kanavos Oct 29 '19 at 16:39

2 Answers2

3

There are plenty of helper functions on the monads in LanguageExt, in this case you are trying to specify a default value if in the Left state of the Either otherwise return the value from the Right. IfLeft is for this specific purpose:

var res = myFunc(succeed).IfLeft(new string[0]);
colethecoder
  • 1,139
  • 2
  • 8
  • 26
-1

To my knowledge, you can not have a variable return type. The types are mandatory for the compiler to know wich function to even call or if the context you use it in makes sense.

Also as a general rule, you should not be fighting type checks. They are your best friend, not your enemy. And not having them and agressive implicit conversion is what results in the PHP and JavaScript examples from this comic.

Most solutions work around this having multiple return values, wich can each be of a different type:

1 Create a struct, tuple or class that can have both a sucess and a result, use that as return type. But you kinda already did that. Check the values of success before using the return value. Advanced options would turn the result into a property, wich will throw an exception if sucess is false. See the Task class and BackgroundWorkerCompletedEventArgs.Result for examples of this.

2 Make a shallow class heirarchy. Something like

abstract class BaseReturnValue { }
class FailureValue : BaseReturnValue { }
class SuccessValue : BaseReturnValue { string value; }

The function would return the type BaseReturnValue. But as that one is abstract, you can only use FailureValue or SuccessValue instances in the code. The rest are is checks.

3 Out prameters. Out allows you to have more then one return value, without the need of writing a class. All variants TryParse() use it to return a bool indicating success, while still giving you a parsed integer too.

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • Thanks -- my question's specifically about how to do this using the LanguageExt library, but yes, I could certainly use other tools and structures to do it. – Tim Barrass Oct 29 '19 at 16:36