It is a strong current trend among languages to become more functional. No, not as "actually doing stuff" but in the mathematical sense of the word. I.e., languages more and more treat functions as any other value, and C# is no exception. Before you know it, we might have the expressivity of Lisp anno 1965 at our disposal.
;-)
We saw a first step towards functional programming in C# 2.0, with the syntactic shortcuts for delegates. See my higher-order function library for an example of what can be done in that version of the language.
Since I have hardly had time to digest the new C# 2.0 features, I hope C# 3.0 is a light desert.
There are 4 major additions to the language:
- type inference
- lambda expressions
- mixins
- query expressions
There are other refinements, but they are too minor to discuss this late at night.
An overview of these four new features follow.
Type Inference
As you all know, C# is a strongly typed language. That means that all expressions and variables have a definite static type, decided at compile-time. It does not necessarily mean that the author of these expressions has to express the type. I.e., the system/compiler could infer the type. And indeed it does, that is why you get a type mismatch error if your declared type is incompatible with the type infered for the value. Yeah, yeah, we do have the ad hoc polymorphism provided to us by OOP, enabling the runtime type to be a proper sub type of the static type (infered or declared.) But, such details are not important for this discussion.
;-)
C# 3.0 alleviates the need to explicitly declare a type for a variable in cases where an initializing expression is used. Again, this does not mean that C# is suddenly a dynamic language or suddenly less strongly typed.
So, where the C# 2.0 developer had to type
-
int life = 42;
the C# 3.0 developer can be lazy and type
-
var life = 42;
This might not seem like a huge gain, but consider much more intricate type, as in
-
foreach (var coolGuy in coolGuys)
-
Console.WriteLine(coolGuy + " is a cool guy!");
DO NOT go overboard with this feature and get lazy! It is still nice to see that the developer knows what he is doing, so a type declaration helps out. A "living" documentation. My suggestion is to use it foremost in cases where the type is not given by the "contract" between implementer and user of a method. This is often the case in more complex C++ libraries and I assume it will become more frequent in C# land.
C++ Developers: yes, those Microsoft adaptees got auto before you!
Haskell/O'Caml Developers: they are gaining ground on you!
Lambda Expressions
Wait a minute! We just had those nifty delegate constructs introduced in C# 2.0, which allowed us to write code inline as
-
doSomethingCool(delegate(int x) { return x+1; });
The lambda expressions of C# 3.0 goes one step further in syntactical compactness and in type genericity, or polymorphism. And, this time it is not the ad hoc polymorphism of OOP we are talking about, but real polymorphism.
Let us start with a non-polymorphic example, stolen from my HOF library but using lambda expressions instead of inline delegates.
-
int[] nums = { 3, 7, 4 };
-
HOF.Transform(nums, (int x)=>x+x, doubles);
after which the doubles list holds 6, 14 and 8.
Ok, and now for a polymorphics example, where we simply drop the explicit typing of the lambda variable. Note that this allows the same expression to be used for int and string.
-
// We first need a generic function type
-
delegate R GenFun<R, A>(A arg);
-
-
void applyToSomeArgs(GenFun fun) {
-
int arg1 = 21;
-
string arg2 = "21";
-
}
-
-
void testApply() {
-
var doubler = x=>x+x; // Look, ma, no type!
-
applyToSomeArgs(doubler);
-
}
We also here revealed that the implicit type of such a generic lambda expression can be converted to the "free" unary delegate. The actual type is actually an expression type (Expression<....>), but that is outside the scope of this tiny introduction.
Lambda expressions can also accept multiple variables, and if a simple expression cannot describe the beauty we assign a function, we can always use a block of statements, with an ending return. Look at:
-
var f =
-
(int x, int y) => {
-
int mul = x*y; int div = x/y; return mul+div;
-
};
-
int res = f(20,5); // res = 104
Mixins
Even C# 2.0 started this strange - but quite fruitful - route of allowing a type to be defined, in effect, in various loci. Remember the partial class definitions?
C# 3.0 takes this a slight step forward via a syntactic sugar framework called extension classes. The way they do that is slightly resemblant to Objective-C's mechanism of mixins, or - with a pair of shaded glasses - to Ruby's mixins.
C++ Developers: yes, I know, you can achieve the same "after the fact" extensions by proper type traits and free functions. You guys are so generic!
;-)
Imagine that we are not satisfied with the dichotomy between string and Regex and that we would like to see the former type host a Match method to check for the matching of a given regular expression. Imagine no more. In C# 3.0, we can achieve that extension by
-
namespace Davber.CoolExtensions {
-
-
using System.Text.RegularExpressions;
-
-
public static class SomeExtensions {
-
public static bool Match(this string text, string match) {
-
return regex.IsMatch(text);
-
}
-
}
-
}
It is the this keyword before the type of the Match method that does it.
Does this mean that string's behavior is forever changed, and all legacy code will break? No, this extension method - which is marked by the this keyword in that first parameter along with residing in a static class - will be accessible for the compiler only if it is brought into the environment. That is achieved via a regular using declaration.
So, the following program will use this "extended" string class.
-
// the following will bring in ALL extension methods for all types
-
using Davber.CoolExtensions;
-
-
public class ExtTest {
-
public static void Main(string[] args) {
-
string text = args[1];
-
string regex = args[2];
-
bool match = text.Match(regex);
-
Console.WriteLine("The text '{0}' did {1} match regex '{2}'",
-
text, match ? "" : "not ", regex);
-
}
-
}
So, all extension methods will be brought into scope. Note that these extension methods are always static and will not adorn the type nor instances in any way. In fact, the expression obj.method(args...) will be rewritten as ExtensionClass.method(obj, args...), with ExtensionClass being the class in which method happened to be found.
Query Expressions
Do you long for the versatility and beauty of SQL even when leaving that Oracle administration GUI? Then this extension is for you! Now you can query your way through webs of objects.
The syntax is quite close to SQL but yet different enough to have to learn a new sub language. That sub language is LINQ. Ah, just what I needed... For a more technical introduction to LINQ, see this blog; or how to solve Sudoku with LINQ.
Say you have a number of clients, each hosting some consultants on site. You could get a view of your big clients, say those hosting 5 or more consultants, via the following query expression:
-
var myBigClients =
-
-
from con in consultants
-
group con by con.Client into client
These LINQ expressions are transformed into functional expressions. I argue that a proper higher-order function library is at least as expressive - and much more modular/recombinant - than LINQ. But, then again, I have only dealt with SQL for the last 20 years, so I might change my mind when those skills of mine get more mature.
Haskell Developers: ok, I confess, I do turn to list comprehensions now and then, which is as close as FPers come to LINQ, so maybe it is not that bad after all.
:-|
All in all, I cannot wait to create a proper higher-order function library for C# 3.0, much along the line of what I did in C# 2.0. This even though, as a language, it is still beaten severly by languages like Haskell and Scala. And laughed at by Ruby. Let us defend this underdog C#!
DISCLAIMER: I have not used C# 3.0, but only read about these features, so there might be slight discrepancies. I will get back to you when I have actually tried them out.
:-|