C# 扩展方法和 F# 管道转发运算符之间有什么关系?
在使用 F# 解决了一些小问题之后,我发现将 C# 扩展方法视为“将 .进入管道转发运算符'。
例如,给定名为 ints 的 Int32 序列,C# 代码:
ints.Where(i => i > 0)
.Select(i => i * i)
类似于 F# 代码
let where = Seq.filter
let select = Seq.map
ints |> where (fun i -> i > 0)
|> select (fun i -> i * i)
事实上,我经常将 IEnumerable 上的扩展方法视为简单的函数库,提供与 F# 的 Seq 模块类似的功能。
显然,管道参数是 F# 函数中的最后一个参数,但却是 C# 扩展方法中的第一个参数 - 但除此之外,在描述扩展方法或通过管道转发给其他开发人员时使用该解释是否存在任何问题?
我会误导他们吗?或者这是一个有用的类比?
After using F# for a few small problems, I've found it helpful for myself to think of C# extension methods as 'a way of turning the . into a pipe-forward operator'.
For example, given a sequence of Int32s named ints, the C# code:
ints.Where(i => i > 0)
.Select(i => i * i)
is similar to the F# code
let where = Seq.filter
let select = Seq.map
ints |> where (fun i -> i > 0)
|> select (fun i -> i * i)
In fact, I often think of the extension methods on IEnumerable as simply a library of functions that provide similar functionality to F#'s Seq module.
Obviously the piped parameter is the last parameter in an F# function, but the first parameter in a C# extension method - but apart from that, are there any issues with using that explanation when describing extension methods or pipe-forward to other developers?
Would I be misleading them, or is it a helpful analogy?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我不会做这样的类比,因为扩展方法可以像实例方法一样被调用,而管道在语法上显然有很大不同。此外,F# 还具有扩展方法(以及扩展属性和事件!),因此我认为确实有可能造成混乱。
然而,我认为管道样式和扩展方法确实都允许将计算流畅地描述为一组步骤,这可能就是您所追求的相似之处。从概念上讲,最好让代码反映出“采用这组整数,仅保留满足 i>0 的子集,然后对每个子集进行平方”的想法,这就是用英语描述任务的方式。管道语法和 C# 扩展方法都允许此类操作。
I wouldn't make that analogy because an extension method can be called as if it were an instance method whereas piping is clearly syntactically quite different. Additionally, F# also has extension methods (as well as extension properties and events!), so I think there's a real possibility of causing confusion.
However, I think that it is true that both the piping style and extension methods allow computations to be described fluently as a set of steps, which is probably the similarity that you are driving at. Conceptually, it is nice have the code reflect the idea of "take this set of ints, keep only the subset satisfying i>0, and then square each of those", which is how the task might be described in English. Both the piping syntax and C# extension methods allow this sort of thing.
我也认为这是一个非常有用的类比。事实上,我在《现实世界函数式编程》一书中描述管道操作符时正是使用了这个类比(它试图向具有 C# 背景的人解释函数式思想)。下面是第六章的引用。
关于两者之间的差异 - 有一些概念上的差异(例如扩展方法“向对象添加成员”),但我认为这对我们在实践中使用它们的方式没有任何影响。
C# 扩展方法和 F# 函数之间一个显着的实际区别是编辑器支持 - 当您键入“.”时。在 C# 中,您可以看到特定类型的扩展方法。我相信,当您键入
|>
时(原则上),F# IntelliSense 也可以显示过滤列表,但这可能需要更多工作,而且尚不支持。这两个构造都用于启用基于表达式的组合编程风格。我的意思是,您可以将更大的代码部分编写为描述应该做什么的单个表达式(如果没有扩展方法/管道操作符,您可能会将代码分解为多个词干)。我认为这种编程风格通常会产生更具声明性的代码。
I also think that this is a very useful analogy. In fact, I used exactly this analogy when describing the pipelining operator in my Real World Functional Programming book (which tries to explain functional ideas to people with C# background). Below is a quote from Chapter 6.
Regarding the differences between the two - there are some conceptual differences (e.g. extension methods "add members to objects"), but I don't think this has any impact on the way we use them in practice.
One notable practical difference between C# extension methods and F# functions is editor support - when you type "." in C#, you can see extension methods for the particular type. I believe that F# IntelliSense could show a filtered list when you type
|>
(in principle) as well, but it is probably much more work and it isn't supported yet.Both of the constructs are used to enable expression-based compositional programming style. By this I mean that you can write much larger portions of code as a single expression that describes what should be done (without extension methods/pipelining operator, you would probably break code into multiple stements). I think that this style of programming generally leads to a more declarative code.
如果您希望重用这些管道,您可能还需要查看反向函数组合运算符。
You may also want to look at the reverse function composition operator if you wish to reuse these pipelines.