Автор 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;