我正在使用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个,这将很好地工作,这使我更加困惑。
这是解决此类问题的错误方法吗?还有我不知道的另一种方式吗?
答案 0 :(得分:2)
Lang-ext作者在这里。我们一直在讨论lang-ext github repo上的问题。
这些是我的评论:
很难。老实说,它们并不是真正的误报,因为Either<L, R>
支持+
运算符,因此属于SelectMany
的{{1}}会产生有效的结果,就像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>>
。它们本质上是完全相同的类型,只是它很好地包装了所有绑定语义,因此您再也不会遇到这个问题。
我正在为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;
}
}