08 November 2005
A good friend of mine and I are looking at taking on a new project together, and as part of the discussion we were exploring some of the differences of taking a relational perspective against an object perspective, and one of the comments she made was that in a relational model, you can always "filter" the data you want based on some predicate. "Ha!", I said, "If that's what you want, I can give you that over objects, too!"
What's more, thanks to generics, I can do this for any collection type in the system without having to introduce it on some kind of base class:
static class SetUtils
{
public static List<T> Project<T>(List<T> list, Predicate<T> pred)
{
List<T> results = new List<T>();
foreach (T p in list)
if (pred(p))
results.Add(p);
return results;
}
// Not too hard to imagine the other relational operators here, too
}
// Usage:
class Person
{
private string firstName;
private string lastName;
public Person(string fn, string ln, int age) {
this.firstName = fn;
this.lastName = ln;
}
public string FirstName {
get { return firstName; }
set { firstName = value; }
}
public string LastName {
get { return lastName; }
set { lastName = value; }
}
public override string ToString() {
return "[Person [" + firstName + "]" + " " + "[" + lastName + "]" + "]";
}
}
class Program {
static void Main(string[] args) {
Person cg = new Person("Cathi", "Gero", 35);
Person tn = new Person("Ted", "Neward", 35);
Person sg = new Person("Stephanie", "Gero", 12);
Person mn = new Person("Michael", "Neward", 12);
List<Person> list = new List<Person>();
list.Add(cg);
list.Add(tn);
list.Add(sg);
list.Add(mn);
List<Person> newards =
SetUtils.Project<Person>(list,
delegate (Person p) { if (p.LastName == "Neward") return true; else return false; } );
foreach (Person p in newards)
Console.WriteLine(p);
}
}
Any more questions? (This is why having (1) a system that supports managed function pointers directly and (2) a generics system that doesn't rely on type erasure is so powerful. Hint, Hint, Sun guys....)
Now if I could just figure out how C# 3.0 manages to differentiate/overload between delegate instances and Expression objects in LINQ/DLinq, I might be able to backport that to C# 2.0, too, and be able to pass these Predicate instances across the wire for execution on other machines.
In a lot of ways, the Predicate delegate type is an example of using C#'s anonymous methods as a form of closure or lambda expression. (It's been argued that anonymous methods-as-delegates aren't "true" closures, since the local variables referenced in a closure will only be references to the objects, not complete copies, but to my mind that's exactly as it should be, as any time you pass a reference to an object, you're passing just that--a reference to an object, not a complete copy of the object. To do otherwise in anonymous methods would violate the Principle of Least Surprise, IMHO.) The Ruby syntax arguably isn't any more elegant or terse, and I suspect similar things could be done in C++ using templates; probably something along these lines already exists in Boost. But alas, I see no way to do this in Java given the current state of the JVM, namely the aforementioned lack of "managed functors" and type-preserving generics. If any out there in Java-land know otherwise, please holler, because I would really love to know how to do this as elegantly.
Last modified 08 November 2005