- First off, it only works on function specially declared to be "extensions", which means your composition options are limited to whatever the library has built in. I can't send arguments to arbitrary static methods, like, say, "someVar.Console.WriteLine".
- Second, since extension methods are defined solely by their method name, ambiguity is quite easy to come across. There's no way to qualify Foo.Function versus Bar.Function.
Pipeline Other languages approach this with two simple things (which actually simplify the entire language/type system overall). First off, we need to be able to define function operators. I'll demonstrate with F#'s pipeline operator: let (|>) x f = f x
[As a side note, any language that lets you toss around operators and functions will allow this kind of syntax – it isn’t that F# had to have compiler support for this particular operator.]
In fake C#, it’d be something like (for fun, notice the lack of type inference):
B operator|> <A, B>(A x, Func<A, B> f) { return f(x); } This means that the |> operator will take x on the left and apply it to f on the right side. If this existed in C#, you'd be able to write something like: "Hello" |> Console.WriteLine or if (myInts |> Enumerable.Any) { .... }
Now we can pass in a parameter to static methods. But, hey, whaddya know? With this, Extension Methods are solved for all single-argument static methods! That was easy. But what about Select – it takes two parameters, so this won’t work. Enter the Lambda What if ALL functions took one parameter and output one parameter? If that were the case, then we’d be set. But how do allow more than one parameter? Well, what if, every time you declared a method with more than one parameter, it actually returned a method that took the next parameter? For example, we could write “Add” as: Func<int,int> Add(Func<int,int> a) { return b => a + b; }
This is known as the curried form of Add. We’d now call it as: Add(5)(6). We can do cute stuff like “var inc = Add(1)”. But, as the Add declaration shows, in C# this is too unwieldy (and this is a simple example!). The compiler should actually do all this for us, so we can just write our functions normally but use them as if they were written in curried form.
Now, if we simply swap the order of arguments for Enumerable.Select, we have our extension method ready to roll: Enumerable.Select(Func, IEnumerable) can be used so:
var squares = myInts |> Enumerable.Select(i => i * i)
Now the call to Select takes the lambda (i * i) and returns a function that takes an Enumerable. It is then given the myInts, and everyone is happy. This is just a quick, crap, explanation. Google can lead you to many more interesting resources about partial application, currying and so on.
Remember Me