带有多个 from 子句的 Either 的 language-ext 任务
language-ext Task of Either with multiple from clauses
我正在学习 FP with 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;
}
}
}
我收到这个错误:
Multiple implementations of the query pattern were found for source type Task<Either<Exception, int>>
. Ambiguous call to 'SelectMany'.
我通过阅读 this webpage 了解到编译器认为问题出在哪里。但是,我显然遗漏了或不理解一些重要的事情,因为我无法弄清楚这种情况是如何导致这个错误的,也不知道该怎么办。如果它只有 2 个来自子句,这将工作得很好,这让我更加困惑。
这是解决此类问题的错误方法吗?还有其他我不知道的方法吗?
编译器很难理解 a
的类型应该是什么(int
或 Either<Exception, int>
),因为它在第二个 [=14] 上未被使用=]行。
对于这个特定案例,这是一个非常丑陋的解决方法。但是,对于任何类型,我认为 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;
}
}
这里是 Lang-ext 作者。我们一直在 lang-ext github repo 上讨论这个问题。
这些是我的评论:
很难。老实说,它们并不是真正的误报,因为 Either<L, R>
支持 +
运算符,因此属于 Task<R>
的 SelectMany
将产生有效结果,就像SelectMany
与 Task<Either<L, R>>
一起使用。
基本上 a
、b
和 c
值可以是 int
或 Either<Exception, int>
,具体取决于哪个 SelectMany
实现编译器选择。
整个表达式对所有 SelectMany
扩展都有效,这显然是我们有歧义的原因。
遗憾的是,将 var three = ...
更改为 Either<Exception, int> three = ...
并没有改变推理系统。因为这是编译器混淆的两个可能表达式之间的关键区别。
您可能想做的一件事是使用 OptionAsync<A>
而不是 Task<Either<L, R>>
而不是使用 Task<Option<A>>
,而是使用 EitherAsync<L, R>
。它们本质上是完全相同的类型,除了它很好地包装了所有绑定语义,所以您再也不会遇到这个问题。
我正在经历为 lang-ext 中的所有 monadic 类型创建 *Async
变体的过程。为方便起见,潜在的性能优势,并允许相当于 3 层嵌套的 monad: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);
}
或者,您可以留在 monad 中并且 return 那:
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;
我正在学习 FP with 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;
}
}
}
我收到这个错误:
Multiple implementations of the query pattern were found for source type
Task<Either<Exception, int>>
. Ambiguous call to 'SelectMany'.
我通过阅读 this webpage 了解到编译器认为问题出在哪里。但是,我显然遗漏了或不理解一些重要的事情,因为我无法弄清楚这种情况是如何导致这个错误的,也不知道该怎么办。如果它只有 2 个来自子句,这将工作得很好,这让我更加困惑。
这是解决此类问题的错误方法吗?还有其他我不知道的方法吗?
编译器很难理解 a
的类型应该是什么(int
或 Either<Exception, int>
),因为它在第二个 [=14] 上未被使用=]行。
对于这个特定案例,这是一个非常丑陋的解决方法。但是,对于任何类型,我认为 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;
}
}
这里是 Lang-ext 作者。我们一直在 lang-ext github repo 上讨论这个问题。
这些是我的评论:
很难。老实说,它们并不是真正的误报,因为 Either<L, R>
支持 +
运算符,因此属于 Task<R>
的 SelectMany
将产生有效结果,就像SelectMany
与 Task<Either<L, R>>
一起使用。
基本上 a
、b
和 c
值可以是 int
或 Either<Exception, int>
,具体取决于哪个 SelectMany
实现编译器选择。
整个表达式对所有 SelectMany
扩展都有效,这显然是我们有歧义的原因。
遗憾的是,将 var three = ...
更改为 Either<Exception, int> three = ...
并没有改变推理系统。因为这是编译器混淆的两个可能表达式之间的关键区别。
您可能想做的一件事是使用 OptionAsync<A>
而不是 Task<Either<L, R>>
而不是使用 Task<Option<A>>
,而是使用 EitherAsync<L, R>
。它们本质上是完全相同的类型,除了它很好地包装了所有绑定语义,所以您再也不会遇到这个问题。
我正在经历为 lang-ext 中的所有 monadic 类型创建 *Async
变体的过程。为方便起见,潜在的性能优势,并允许相当于 3 层嵌套的 monad: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);
}
或者,您可以留在 monad 中并且 return 那:
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;