Groovy features in Nice
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();
}
]
