我正在阅读Tomas Petricek 和 Jon Skeet所著的Real-world functional programming一书,我很难消化关于计算表达式1)(又名 monads)的部分。
通过这本书,我了解到——与我以前的经验相反——LINQ 查询表达式不限于IEnumerable<T>
,而是也可以在其他自定义类型上工作。这对我来说似乎很有趣,我想知道是否存在查询表达式语法 ( from x in ... select ...
) 非常适合的场景。
一些背景资料:
显然,这种自定义类型被称为计算类型,它们被描述为与Haskell中的monad基本相同。我一直无法理解 monad 到底是什么,但根据书中的说法,它们是通过两个称为bind和return的操作来定义的。
在函数式编程中,这两个操作的类型签名将是(我认为):
// 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 计算类型:
使用这些知识,我现在可以编写一个自定义计算类型,它不是 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));
}
}
现在我可以Wrapped<T>
在 LINQ 查询表达式中使用,例如:
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 页开始。