Ambition, Quaere, Closures and Other Random Thoughts

Published 27 September 07 07:20 PM | andersnoras 

Yesterday I came across Chris Wanstrath Ambition project which is very similar to my Quaere project.  To exemplify the similarities between LINQ, Ambition and Quaere, here is the same query expressed in each "language".

LINQ

    var expensiveProducts =
            from p in products
            where p.UnitsInStock > 0 && p.UnitPrice > 3.0
            select p;
    // ...and it can also be written this way...
    var expensiveProducts = 
        p.Select(p => p.UnitsInStock > 0 && p.UnitPrice > 3.0);

Ambition

     expensiveProducts = Product.select { |p|
         p.unitsInStock > 0 && p.unitPrice > 3.0
     }

Quaere

    Iterable<Product> expensiveProducts = 
        from("p").in(products).
        where(gt("p.getUnitsInStock()",0).and(gt("p.getUnitPrice()",3.0))).
        select("p");

Quaere is the most verbose of these, and Ambition is very similar to using LINQs "Select" extension method. Ambition uses closures to define the predicates, and this is kind of similar to Groovy's findAll operation;

    products.findAll { it.getUnitsInStock() > 0 && it.getUnitPrice() > 3.0}

In The Poor Man's Lambda - Revisited I explained how I moved from using anonymous classes, to expressions to define predicates in Quaere. Apart from the readability aspect, I also need to get the abstract syntax tree for the predicate so that I make it part of the expression tree used to describe the query. Ambition uses ParseTree to get the AST of the closure. Thomas Mueller and Carl Rosenberger have come up with some interesting ideas on how we can improve Quaere's type safety. Thomas has written a small API which uses an Alias class rather than the current string based variable references used in Quaere, if we implement this the above query will turn into something like this:

    Alias<Product> p=new Alias<Product>(products);
    Iterable<Product> expensiveProducts =
        from(p).in(products).
        where(p.invoke("getUnitsInStock").gt(0).and(p.invoke("getUnitPrice").gt(3.0))).
        select(p);

A further option is to switch back to anonymous classes and use these to write predicates. We could probably also use a proxy to inject the necessary metadata into the alias while keeping the alias type safe. 

    final Product p=Alias.define(Product.class);
    Iterable<Product> expensiveProducts =
        from(p).in(products).
        where(new Function() {
            public boolean fn() {
                return p.getUnitsInStock() > 0 && p.getUnitPrice() > 3.0;
            } 
        }).
        select(p);

We could probably use an ASM MethodVisitor to retrieve the AST for the fn() method and convert it into an expression tree fragment (shown below).

expression

Still I'm not comfortable with all the noise added to the query expression by using an anonymous class because it makes the whole thing look rather arcane. So until we get closures in Java, I don't see this as a good option. But when we do, we could write something like this:

    Iterable<Product> expensiveProducts = 
        from(Product.class).in(products).
        where(
            {Product p => p.getUnitsInStock() > 0 && p.getUnitPrice() > 4.0 }).
        select();

We'd still need to get the AST, if we're really lucky maybe this will be as easy as it is in Groovy, otherwise ASM is probably the way to go.

Filed under: , , , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Carl Rosenberger said on September 28, 2007 8:30 AM:

Whenever you consider working on AST level, you may want to look into bloat also:

https://sourceforge.net/projects/javabloat

We (db4o.com that is) chose it over asm and BCEL when we looked into bytecode analysis libraries and we are contributing to making sure that it works against latest Java.

Our code that uses bloat (GPLed) is available in our SVN in the db4onqopt project:

https://source.db4o.com/db4o/trunk/

I didn't contribute to that code myself, so I don't have an opinion how good it is. :-)

# Baard Johansen said on September 29, 2007 2:36 AM:

For those who didn't recognise the Ruby-syntax (me included), Ambition is written in Ruby.

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below