language-ext Задача Either с несколькими предложениями from - PullRequest
0 голосов
/ 09 сентября 2018

Я изучаю FP с language-ext и столкнулся с проблемой, которую мне не удалось преодолеть. Я упростил свой код до этого примера:

using System;
using System.Threading.Tasks;
using LanguageExt;
using static LanguageExt.Prelude;
using Xunit;

namespace Temp {

    public class SelectManyError {

        [Fact]
        public async Task Do() {

            var six = await from a in Task.FromResult(Right<Exception, int>(1))
                            from b in Task.FromResult(Right<Exception, int>(2))
                            from c in Task.FromResult(Right<Exception, int>(3))
                            select a + b + c;

        }
    }
}

Я получаю эту ошибку:

Для типа источника Task<Either<Exception, int>> найдено несколько реализаций шаблона запроса. Неоднозначный вызов «SelectMany».

Я понимаю, что, по мнению компилятора, связано с чтением этой веб-страницы . Но я явно упускаю или не понимаю что-то важное, потому что не могу понять, как эта ошибка вызвана этим сценарием ИЛИ что с этим делать. Это будет прекрасно работать, если в предложениях только 2, что еще больше смущает меня.

Это неправильный подход к проблеме такого типа? Есть ли другой способ, о котором я не знаю?

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Автор Lang-ext здесь. Мы обсуждали эту проблему на репозитории lang-ext github .

Вот мои комментарии:

Это сложно. Честно говоря, это не ложные срабатывания, потому что Either<L, R> поддерживает оператор +, и поэтому SelectMany, принадлежащий Task<R>, даст правильный результат, такой же как SelectMany, который работает с Task<Either<L, R>>.

В основном значения a, b и c могут на законных основаниях быть int или Either<Exception, int> в зависимости от того, какую реализацию SelectMany выберет компилятор.

Полное выражение действительно для всех SelectMany расширений, поэтому, очевидно, мы имеем эту двусмысленность.

Жаль, что изменение var three = ... на Either<Exception, int> three = ... не изменит систему логического вывода. Потому что это ключевое различие между двумя возможными выражениями, из-за которых компилятор смущен.

Единственное, что вы можете сделать вместо использования Task<Option<A>>, это использовать OptionAsync<A> и вместо Task<Either<L, R>> использовать EitherAsync<L, R>. По сути, они абсолютно одинакового типа, за исключением того, что все семантики связывания красиво обернуты, поэтому у вас никогда не возникнет этой проблемы.

Я прохожу процесс создания варианта *Async для всех монадических типов в lang-ext. Для удобства, потенциальные преимущества в производительности и допустимый эквивалент 3 вложенных уровней монад: M<A<B<C>>>, например, Seq<OptionAsync<A>> - это то же самое, что Seq<Task<Option<A>>>.

.

В любом случае, вернувшись к приведенному выше примеру, вы могли бы вместо этого сделать:

public async Task<int> Method()
{
    var six = from a in Right<Exception, int>(1).ToAsync()
              from b in Right<Exception, int>(2).ToAsync()
              from c in Right<Exception, int>(3).ToAsync()
              select a + b + c;

    return await six.IfLeft(0);
}

Или, если вы хотите построить из Task<int>:

public async Task<int> Method()
{
    var six = from a in RightAsync<Exception, int>(Task.FromResult(1))
              from b in RightAsync<Exception, int>(Task.FromResult(2))
              from c in RightAsync<Exception, int>(Task.FromResult(3))
              select a + b + c;

     return await six.IfLeft(0);
}

Или вы можете остаться внутри монады и вернуть это:

public EitherAsync<Exception, int> Method() =>
    from a in RightAsync<Exception, int>(Task.FromResult(1))
    from b in RightAsync<Exception, int>(Task.FromResult(2))
    from c in RightAsync<Exception, int>(Task.FromResult(3))
    select a + b + c;
0 голосов
/ 10 сентября 2018

Компилятору трудно понять, какой тип a должен быть (int или Either<Exception, int>), поскольку он не используется во второй строке from.

Вот ужасно уродливый обходной путь для этого конкретного случая. Однако, для любого типа, я думаю, что взломать можно адаптировать для работы для этого типа.

using System;
using System.Threading.Tasks;
using LanguageExt;
using Xunit;
using static LanguageExt.Prelude;

public class Namespace
{
    [Fact]
    public async Task Method()
    {
        var six = await from a in Right<Exception, int>(1).AsTask()
                        from b in Right<Exception, int>(a - a + 2).AsTask()
                        from c in Right<Exception, int>(3).AsTask()
                        select a + b + c;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...