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?
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.