Permalink

Groovy features in Nice

07 AUG 2004

Over on Lambda, Mario B. made a comment to the effect that in comparing Nice and Groovy, he thought that Groovy had more features. I don't mean any disrespect to the Groovy folks, but I think Nice supports most of the features on the Groovy homepage at least as well as Groovy. Here's my proof.

Groovy Markup

Here's what Groovy looks like building a XML document or something else with a tree structure:


someBuilder = new NodeBuilder()



someBuilder.people(kind:'folks', groovy:true) {

  person(x:123,  name:'James', cheese:'edam') {

    project(name:'groovy')

    project(name:'geronimo')

  }

  person(x:234,  name:'bob', cheese:'cheddar') {

    project(name:'groovy')

    project(name:'drools')

  }

}

The critical features here are syntactic. You have to be able to support keyword arguments, and you have to support trailing block closures. The rest is library work. Nice has both these features. I can demonstrate the library support if anybody's interested, but I'll skip it here for the time being.

Path expression language

This is a convenient way of dealing with complicated object structures. The syntax will look a little bit different with Nice, but it's equally convenient. Some examples:

Groovy

if (customer.orders.any { it.amount > 1000 && 

	it.product.type == "cheese"} ) {

  doSomething();

}

Nice

if (customers.orders.any(Order ord => ord.amount > 1000 && 

	ord.product.type == "cheese")) {

  doSomething();

}

Note that the only difference is the need to specify a name for the argument ("ord"), as opposed to always having to use a special name ("it")[1]. Don't be dismayed by the need to declare a type for ord ("Order"), Nice does type inference almost everywhere else, and I'm hoping Daniel will implement it for anonymous methods like these before the 1.0 release. Then it will be possible to omit it and still have type safety:

Nice (future)

if (customers.orders.any(ord => ord.amount > 1000 && 

	ord.product.type == "cheese")) {

  doSomething();

}

Groovy

if (customers.any { it.location.code == "UK" && 

	it.orders.any { it.amount > 1000 && 

		it.product.type == "cheese"}} ) {

  doSomething();

}

Nice

if (customers.any(Customer c => c.location.code == "UK" && 

	c.orders.any(Order o => o.amount > 1000 &&

		o.product.type == "cheese")) ) {

  doSomething();

}

In this example, being able to choose a name other than "it" improves readability, I think.

Groovy

for (order in customers.findAll { it.location.code == "UK" }.orders) {

  println("order ${order.id} has value ${order.value}");

}

Nice

for (order : customers[

	Customer it => it.location.code == "UK"].map(orders).concat) {

  println("order " order.id " has value " order.value);

}

Operator Overloading

Groovy lets you overload the builtin operators. This means that it translates a + b into a.plus(b), so that you can define your own plus() method. This is a useful feature, but it is limited in that the type of the first object in the expression determines which plus method will be called! This means that a + b may not be the same as b + a, if a and b both define plus() methods. Nice doesn't have this limitation, since it is based on multiple dispatch. This means that the most applicable method for a given call is chosen based on the types of all the arguments involved. Nice's syntax for this is more self-explanatory as well since it uses the operator symbol directly.

Groovy


class MyClass {

  /*..*/

  MyClass plus(MyClass other) { /*..*/ }

}

Nice

class MyClass { /*..*/ }

MyClass `+` (MyClass lhs, MyClass rhs) { /*.. */ }

Closures

First class methods you can pass around, not related to any particular class. Nice is an object/functional language, with first class multi-methods and anonymous methods. Syntax differs, of course, but both are equally convenient.

Polymorphic iteration and autoboxing

Nice supports autoboxing sanely. Better than even Java 1.5 does. See Daniel's article for details. Not sure what "polymorphic iteration" means, so that will have to wait for later.

Well, that's enough for one day. More later.

[Update: Jeremy Rayner pointed out that you can use a different name in Groovy, like this:


if (customers.any { c | c.location.code == "UK" && 

	c.orders.any { o | o.amount > 1000 && 

		o.product.type == "cheese"}} ) {

  doSomething();

}
]