5

几天前,我一直在#haskell IRC 房间里闲逛,有人提到 C# 具有执行单子理解的语法。这是什么意思?

如果我理解正确,单子理解只是一种排序bind操作的方式,哪种听起来像do符号?那是对的吗?

问题是我在 C# 中看不到这一点。据我所知IEnumerable<T>是一个单子,SelectMany它的bind功能在哪里,因为它的签名是A -> IEnumerable<B>. 有了一点想象力,我们可以做到

from x in xs
from y in ys

这转化为(我在这里不是 100% 确定

xs.SelectMany(x => ys.Select(y => y), (x, y) => ...)

但即使这是真的并且我们认为 LINQ 是一种 monad 理解语法,它仍然只适用于IEnumerable<T>. 我们在 C# 中确实有其他 monad,例如Task<T>,但是我们如何在这些 monad 上使用 LINQ?

这个问题中的许多假设可能是完全错误的,因为我仍在尝试掌握一些单子魔法的东西。如果我错了,请纠正我:)

4

1 回答 1

9

LINQ 查询语法只是语法糖,IEnumerable<>对 .

如果您查看C# 语言规范,它会在第 7.16.2 节中描述应如何转换 LINQ 的查询表达式

C# 语言不指定查询表达式的执行语义。相反,查询表达式被转换为遵循查询表达式模式(第 7.16.3 节)的方法调用。具体来说,查询表达式被转换为名为 Where、Select、SelectMany、Join、GroupJoin、OrderBy、OrderByDescending、ThenBy、ThenByDescending、GroupBy 和 Cast 的方法的调用。这些方法应具有特定的签名和结果类型,如 § 7.16.3. 这些方法可以是被查询对象的实例方法,也可以是对象外部的扩展方法,它们实现了查询的实际执行。

您的具体示例描述为

带有第二个 from 子句的查询表达式,后跟一个 select 子句

from x1 in e1
from x2 in e2
select v

被翻译成

( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v )

因此,使用您示例中的变量名称,任何xs具有方法的变量Treturned SelectMany(Func<Tx,Tys>, Func<Tx,Ty,Treturned>)都可以在如下语句中使用

Treturned returned =
    from x in xs
    from y in ys
    select r;

这将准确编译

Treturned returned = xs.SelectMany(x => ys, (x, y) => r);

确实如此,这就是在 xs 上存在这种方法的任何时候。SelectMany存在的事实IEnumerable<>并不妨碍我们为其他类型配备具有相同名称的方法或扩展方法。

C# 可以从它知道是什么的事实推断 lambda 的类型xs,并从中可以查找类型到xs's的参数SelectMany

于 2013-10-31T15:47:02.520 回答