【问题标题】:Monadic comprehension syntax in C#C# 中的一元理解语法
【发布时间】:2013-11-11 15:48:13
【问题描述】:

几天前我一直在#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 是一种单子解析语法,它仍然只适用于IEnumerable<T>。我们在 C# 中确实有其他单子,例如 Task<T>,但我们如何在这些单子上使用 LINQ?

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

【问题讨论】:

    标签: c# haskell functional-programming monads


    【解决方案1】:

    LINQ 查询语法只是语法糖,对IEnumerable<> 一无所知,这就是为什么您可以将它用于其他事情的原因。

    如果您查看C# language specification,它会在第 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 )
    

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

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

    这将准确编译

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

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

    C# 可以根据它知道 xs 是什么这一事实推断 lambda 的类型,并据此可以查找 xsSelectMany 的参数的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-27
      • 1970-01-01
      • 1970-01-01
      • 2012-08-21
      • 2013-06-22
      • 1970-01-01
      相关资源
      最近更新 更多