Different versions of the Operate method I have code that basically is the same, but depending on the number of arguments you provide (up to 5), it will operate with them in a very repetitive way. I have the feeling that this can be optimized in some way, since maintaining and creating all the overloads by hand is a tedious and error-prone task.
The code is 5 versions (overloads) of the Operate method. As you can see, it increasingly deal with 2, 3, 4 and 5 arguments (each one is of a given type), so it can adapt from 2 to 5 type arguments.
public static Either&lt;ErrorList, TResult&gt; Operate&lt;T1, T2, TResult&gt;(Either&lt;ErrorList, T1&gt; a, Either&lt;ErrorList, T2&gt; b,
    Func&lt;T1, T2, Either&lt;ErrorList, TResult&gt;&gt; onSuccess)
{
    var success = from av in a.RightValue
                  from bv in b.RightValue
                  select onSuccess(av, bv);
    var errorList = new[]
        {
            a.LeftValue,
            b.LeftValue,
        }
        .SelectMany(x =&gt; x.ToEnumerable())
        .Aggregate((x, y) =&gt; new ErrorList(x.Concat(y)));

    return success.ValueOr(() =&gt; errorList);
}

public static Either&lt;ErrorList, TResult&gt; Operate&lt;T1, T2, T3, TResult&gt;(Either&lt;ErrorList, T1&gt; a,
    Either&lt;ErrorList, T2&gt; b, Either&lt;ErrorList, T3&gt; c,
    Func&lt;T1, T2, T3, Either&lt;ErrorList, TResult&gt;&gt; onSuccess)
{
    var success =
        from av in a.RightValue
        from bv in b.RightValue
        from cv in c.RightValue
        select onSuccess(av, bv, cv);

    var errorList = new[]
        {
            a.LeftValue,
            b.LeftValue,
            c.LeftValue,
        }
        .SelectMany(x =&gt; x.ToEnumerable())
        .Aggregate((x, y) =&gt; new ErrorList(x.Concat(y)));

    return success.ValueOr(() =&gt; errorList);
}

public static Either&lt;ErrorList, TResult&gt; Operate&lt;T1, T2, T3, T4, TResult&gt;(Either&lt;ErrorList, T1&gt; a, Either&lt;ErrorList, T2&gt; b, Either&lt;ErrorList, T3&gt; c, Either&lt;ErrorList, T4&gt; d,
    Func&lt;T1, T2, T3, T4, Either&lt;ErrorList, TResult&gt;&gt; onSuccess)
{
    var success = from av in a.RightValue
                  from bv in b.RightValue
                  from cv in c.RightValue
                  from dv in d.RightValue
                  select onSuccess(av, bv, cv, dv);

    var errorList = new[]
        {
            a.LeftValue,
            b.LeftValue,
            c.LeftValue,
            d.LeftValue,
        }
        .SelectMany(x =&gt; x.ToEnumerable())
        .Aggregate((x, y) =&gt; new ErrorList(x.Concat(y)));

    return success.ValueOr(() =&gt; errorList);
}

public static Either&lt;ErrorList, TResult&gt; Operate&lt;T1, T2, T3, T4, T5, TResult&gt;(
    Either&lt;ErrorList, T1&gt; a, 
    Either&lt;ErrorList, T2&gt; b, 
    Either&lt;ErrorList, T3&gt; c, 
    Either&lt;ErrorList, T4&gt; d,
    Either&lt;ErrorList, T5&gt; e,
    Func&lt;T1, T2, T3, T4, T5, Either&lt;ErrorList, TResult&gt;&gt; onSuccess)
{
    var success = from av in a.RightValue
        from bv in b.RightValue
        from cv in c.RightValue
        from dv in d.RightValue
        from ev in e.RightValue
        select onSuccess(av, bv, cv, dv, ev);

    var errorList = new[]
        {
            a.LeftValue,
            b.LeftValue,
            c.LeftValue,
            d.LeftValue,
            e.LeftValue,
        }
        .SelectMany(x =&gt; x.ToEnumerable())
        .Aggregate((x, y) =&gt; new ErrorList(x.Concat(y)));
    
    return success.ValueOr(() =&gt; errorList);
}

Is there a better way to do this without repeating almost the same code? Imagine that I would like to create another overload for 6, 7, 8... arguments. It can be really tricky and sub-optimal.
You can try to extract the common piece of code into a helper method:
private static Either&lt;ErrorList, TResult&gt; Operate&lt;TResult&gt;(ErrorList[] errorLists, Either&lt;ErrorList, TResult&gt; success)
{
    var errorList = errorLists
        .SelectMany(x =&gt; x.ToEnumerable())
        .Aggregate((x, y) =&gt; new ErrorList(x.Concat(y)));

    return success.ValueOr(() =&gt; errorList);
}

Then you can reduce the overloads' body only to calculate the helpers inputs:
public static Either&lt;ErrorList, TResult&gt; Operate&lt;T1, T2, T3, T4, T5, TResult&gt;(
    Either&lt;ErrorList, T1&gt; a, Either&lt;ErrorList, T2&gt; b, Either&lt;ErrorList, T3&gt; c, Either&lt;ErrorList, T4&gt; d, Either&lt;ErrorList, T5&gt; e,
    Func&lt;T1, T2, T3, T4, T5, Either&lt;ErrorList, TResult&gt;&gt; onSuccess)
  =&gt; Operate&lt;TResult&gt;(
    new [] { a.LeftValue, b.LeftValue, c.LeftValue, d.LeftValue, e.LeftValue },
    onSuccess(a.RightValue, b.RightValue, c.RightValue, d.RightValue, e.RightValue));