【问题标题】:LINQ query expressions that operate on types (monads?) other than IEnumerable<T> -- Possible uses?对 IEnumerable<T> 以外的类型(monad?)进行操作的 LINQ 查询表达式——可能的用途?
【发布时间】:2011-02-27 17:23:42
【问题描述】:

我正在阅读 Real-world functional programming by Tomas Petricek and Jon Skeet 的书,我很难消化关于计算表达式的部分1)(又名 monads)。

通过这本书,我了解到——与我之前的经验相反——LINQ 查询表达式不限于IEnumerable&lt;T&gt;,还可以用于其他自定义类型。 这看起来很有趣对我来说,我想知道是否存在查询表达式语法 (from x in ... select ...) 非常适合的场景。


一些背景信息:

显然,这种自定义类型被称为计算类型,它们被描述为与monads in Haskell 本质上相同的东西。我一直无法理解 monad 到底是什么,但根据书上的说法,它们是通过称为 bindreturn 的两个操作来定义的。

在函数式编程中,这两个操作的类型签名将是(我认为):

//    Bind      :    M<A'> -> (A' -> B') -> M<B'>
//
//    Return    :    A' -> M<A'>

其中M 是一元类型的名称。

在 C# 中,这对应于:

Func< M<A>, Func<A,B>, M<B> >   Bind;

Func< A, M<A> >                 Return;

事实证明,LINQ 的Enumerable.Select(投影运算符)与M := IEnumerable 的绑定操作具有完全相同的签名。

我的自定义 LINQ 计算类型:

利用这些知识,我现在可以编写自定义计算类型 not IEnumerable:

// my custom computation type:
class Wrapped<A>
{
    // this corresponds to the Return operation:
    public Wrapped(A value)
    {
        this.Value = value;
    }

    public readonly A Value;
}

static class Wrapped
{
    // this corresponds to the Bind operation:
    public static Wrapped<B> Select<A, B>(this Wrapped<A> x, Func<A,B> selector)
    {
        return new Wrapped<B>(selector(x.Value));
    }
}

现在我可以在 LINQ 查询表达式中使用Wrapped&lt;T&gt;,例如:

Wrapped<int> wrapped = new Wrapped<int>(41);

Wrapped<int> answer  = from x in wrapped   // works on int values instead 
                       select x + 1;       // of Wrapped<int> values!

当然,这个例子不是很有用,但它演示了如何查询表达式可以做其他事情而不是使用集合,例如用某种类型包装和展开值。


问题:

上面的计算类型似乎不是很有用。因此我想知道,利用 LINQ 查询表达式还有哪些其他合理的用途(除了处理集合)?


1) 第 12.4 节:“引入替代工作流程”,从第 334 页开始。

【问题讨论】:

  • 顺便说一句,这个问题应该是社区维基吗?
  • 刚刚在 monads 上找到了另一个答案(由 Eric Lippert 撰写),我觉得非常有用:stackoverflow.com/questions/2704652#2704795
  • 顺便说一句:这里的“绑定”类型错误:这是 Haskell 中 fmap 的签名,或者只是列表中的 map,这确实是 Select() 所做的。更有趣的一元绑定是Func&lt;M&lt;A&gt;, Func&lt;A,M&lt;B&gt;&gt;, M&lt;B&gt;&gt;。这是列表上的concatMap,或者等效地是IEnumerable 上的SelectMany()。你写的 identity monad 仍然不会做任何有趣的事情,但是在一个更有趣的 monad 中,所有有趣的事情都会发生在 SelectMany 中。试试 Maybe monad —— 那是“一个包装的值或一个空对象”。
  • @camccann: 感谢您指出正确的类型签名。实际上,在发布后不久我就想到我弄错了Bind。 -- 我确实实现了Maybe monad btw.,并且惊叹于您如何在 LINQ 查询表达式中获得条件评估/执行之类的东西(取决于Maybe 值是否包含值)。但这是唯一一个或多或少适合 LINQ 并做一些明智的事情的有趣 monad。你知道更多与from..in..where..let..select LINQ 语法完美结合的 monad 吗?

标签: c# linq ienumerable monads computation-expression


【解决方案1】:

想法:

  • PushLINQ(我和 Jon) - 将 LINQ 反转为推送模型(而不是 IEnumerable&lt;T&gt; 拉取模型)
  • 反应式框架/反应式扩展 - 另一个非常不同的事件模型,结合了 LINQ 语法
  • 我使用 LINQ 查询语法编写了一个线程 API (ab);我不是 100% 相信它,所以放弃了它——但它很有趣;使用from (SelectMany) 等来挑选分支/合并点完全与枚举无关

【讨论】:

    【解决方案2】:

    虽然我不喜欢这样做(因为感觉有点像作弊),但我想这次我必须回答我自己的问题。

    我对此进行了更多思考。我的问题有点幼稚。它归结为 LINQ 查询表达式(例如 frominwhereselect…)以及 foreach 是其他更基本语法之上的语法糖。

    • foreach 适用于任何实现 IEnumerator&lt;T&gt; GetEnumerator() 方法的东西。 IEnumerable&lt;T&gt; 恰好满足了这个条件。

    • 同样,LINQ 查询表达式是根据一些定义明确的规则进行翻译的,例如from x in xs where x &gt; 0 select -x 变为 xs.Where(x =&gt; x &gt; 0).Select(x =&gt; -x)。只要某些类型实现了部分或全部查询运算符方法,该类型就可以与 LINQ 一起用于几乎任何目的。

    我剩下的问题是,除了处理集合之外,LINQ 可以实际用于什么;我认为答案将是“其他很少”,因为 LINQ 查询表达式的结构非常严格。你总是需要fromin 部分。似乎也总是需要select…。如果生成的表达式的“语言”不适合特定的潜在场景,那么任何其他关键字(letorderbygroupby 等)都不会改善这种情况。 LINQ 在设计时非常明确地考虑了一个目标,即查询数据,而由此产生的语法实际上对 LINQ 的限制可能超出了必要的范围。

    我已经将 LINQ 的可能性与 F# 计算表达式的可能性进行比较,而不是用于查询数据的可能性,并且它们似乎更灵活,因为没有那么多必需的关键字。这往往使它们适用于更多场景。

    【讨论】:

    • 回答你自己的问题还不错,一点也不……反正你也得不到任何代表,所以它只是为别人提供好的信息。而且你确实给了人们大量的时间来回答它 - 然后更多!
    【解决方案3】:

    LinqToTwitter 以一种不同寻常的方式使用 LINQ。从逻辑上讲,'from' 子句中的内容不是可枚举类型。 看看源代码:)

    【讨论】:

      【解决方案4】:

      它很有用的原因与 monad 在 Haskell 中有用的原因相同。例如,实现Either&lt;TLeft, TRight&gt; 类型并为其提供查询理解实现是非常有可能的。这可用于编写更多功能代码,您可以在用例中编写操作,并内置错误处理和日志记录等。基本上,看看 Haskell 之类的语言(或 F# 更贴近家庭的东西),感受一下在实际代码中如何使用 monad。

      现在,问题当然是这可能不是非常惯用的 C# 代码,即使它可以工作且可维护等等。重要的是不要与语言抗争,不要编写只有你能理解的代码。至少,您的团队应该“参与其中”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-16
        • 1970-01-01
        • 2015-03-14
        • 2017-12-24
        • 1970-01-01
        • 2012-10-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多