Saturday, January 22, 2011

New things

Sadly Tangent development often falls behind bills and food. Now that some time has opened up, we'll talk about actually doing some implementation. This blog was designed to step through those processes a little bit. Provide a mechanism to think about the feature, as well as provide some insight into implementation.

The feature that is the focus today is tentatively called Implicit Lambdas. C# 3 added a variety of extension methods to support operations over arbitrary collections. By default, they could look a bit... chunky:

    list.Where(x=>x.Color == Color.Blue).Select(x=>x.Position);

So the language designers introduced specialized syntax to deal with it:

    from x in list where x.Color == Color.Blue select x.Position;

This certainly looks a little better. Unfortunately, that's the only place that has special syntax. Any other methods that take another method as an argument need to use the full lambda syntax. Tangent has two problems with this. The first is that the parsing mechanism does not particularly lend itself to specialized keywords. The second is that Tangent's lambda syntax requires you specify the return type (so that the order of operation inference can work):


Which means that we can't just ignore some sort of cleaner syntax to handle cases where lambdas make sense.

Enter Implicit Lambdas. The idea here is that since Tangent knows what method is taking the other method as an argument, we can let it specify what types it wants. Further, since almost every C# lambda ends up with something repetitive like:

    x => x.Color == Color.Blue

We can add some implicit scoping (and variable naming) to the mix. No need to specify x. It essentially (and under the covers) acts as though a new member method is being made on the fly for the type we're making a lambda for. So if we were to define our own where function in Tangent, it'd probably go like this:

    generic(any:T) (IEnumerable<T>: collection) where 
                   (implicit T -> bool: predicate) => yields<T> {
        foreach T:entry in collection {
            if predicate(entry) {
                yield entry;

The modifier implicit here tells the compiler that we can use the automatic scope for that parameter. It's not automatic, because it doesn't make sense for everything. Sometimes it's useful to have the callsite explicitly show that there's a lambda there. So in Tangent, the linq query can be reduced to:

    list where Color == Blue select Position;

(assuming the Tangent convention of having Blue be a global/overload for the color, and select has had a similar treatment).

This feature does lack a little bit where the LINQ syntax does not. It doesn't deal well with multiple variables really; you'll need to have the implicit type be a key, which will probably be awkward to deal with at the callsite. It doesn't deal well when the implicit type you're acting on is actually the value you want to work with. Something like 'double every odd number in the list' is really awkward as it's currently designed.

On the upside this provides an arbitrary, user-definable way to capture expressions without nasty lambda syntax everywhere.