多个from子句的language-ext Task of Either

时间:2018-09-09 19:14:51

标签: language-ext

我正在使用language-ext学习FP,但遇到了一个我无法克服的问题。我将代码简化为以下示例:

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”的歧义调用。

通过阅读this webpage,我了解编译器认为问题出在哪里。但是,我很明显缺少或不了解一些重要的东西,因为我无法弄清楚这种情况是如何引起此错误的,或者该怎么做。如果只有from子句中的2个,这将很好地工作,这使我更加困惑。

这是解决此类问题的错误方法吗?还有我不知道的另一种方式吗?

2 个答案:

答案 0 :(得分:2)

Lang-ext作者在这里。我们一直在讨论lang-ext github repo上的问题。

这些是我的评论:

很难。老实说,它们并不是真正的误报,因为Either<L, R>支持+运算符,因此属于SelectMany的{​​{1}}会产生有效的结果,就像Task<R>SelectMany一起使用。

基本上,Task<Either<L, R>>ab的值可以合法地为cint,具体取决于编译器选择哪种Either<Exception, int>实现方式。

整个表达式对所有SelectMany扩展名都有效,这显然就是我们有歧义的原因。

可惜的是,将SelectMany更改为var three = ...并不会改变推理系统。因为这是编译器混淆的两个可能表达式之间的关键区别。

您可能要使用Either<Exception, int> three = ...而不是Task<Option<A>>而不是OptionAsync<A>来使用Task<Either<L, R>>。它们本质上是完全相同的类型,只是它很好地包装了所有绑定语义,因此您再也不会遇到这个问题。

我正在为lang-ext中的所有单子类型创建一个EitherAsync<L, R>变体的过程。为方便起见,可能会提高性能,并允许相当于3个嵌套级别的monad:*Async例如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>

或者,您可以留在monad中并返回:

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);
}

答案 1 :(得分:1)

由于第二个a上未使用int的类型(Either<Exception, int>from),因此编译器很难理解线。

在此特定情况下,这是一个非常丑陋的解决方法。但是,对于任何类型,我都认为该hack可以适用于该类型。

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;
    }
}