Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Groovy reduces typing to a second-class citizen, and I think it is very reasonable to use that as an excuse to eliminate the traditional type-cast syntax. In a dynamically-typed language, it is very difficult to know what is a type and what is a variable, so any syntax that allows one to be mistaken for the other is bad for both readability and parsing.

The "as" operator

I propose we add "as" as an operator which will be used in place of the traditional type-cast operator, and which can be overloaded on the class for doing things like automatic type conversions.

Code Block
groovy
groovy
x = new InputStream()
y = x as Reader

In the above example, InputStream would define an asReader() method, which would be used by the compiler to do the type conversion. The compiler itself does the type cast, of course.

As "as" is an infix operator that has specific operand requirements, we eliminate the ambiquities of what is a type and what isn't. Consider the following code:

Code Block
groovy
groovy
def method( x )
{
     y = (Test)+x
}

Whether that is a type cast or an addition is less-than-trivial to figure out during compilation. Further, if Test is a class in the local package, it might be totally ambiguous to the casual reader, too.

The same code with the new syntax is much more obvious:

Code Block
groovy
groovy
def method( x )
{
    y = x as Test
}

The problem gets worse if the class name starts with a lower-case letter and proper scoping is in effect:

Code Block
groovy
groovy
b = SomeBuilder()
b {
    p( (test)+a )
}

Is that an addition of two variables, a type cast, or the addition of the result of the delegate's getTest() accessor and a? It is utterly impossible to say until runtime, if proper scoping is in effect.

For complex cases, "as" is mainly neutral on readability (it reduces parenthesis counts, but calls less attention to the purpose of the code). Compare:

Code Block
groovy
groovy
x = ((MyClass)list.get(0)).doStuff()

to:

Code Block
groovy
groovy
x = (list.get(0) as MyClass).doStuff()

A further benefit of this change is that if we allow class names to be overridden by local variables, type-casting will still be possible:

Code Block
groovy
groovy
import scripts.myscript

def method( x )
{
    myscript = new myscript()
    y = +x as myscript
}

Maybe not advisable, but all unambiguous.

Effect on optionals

From the viewpoint of preserving optionals, changing to "as" is important as it eliminates an overload for parenthesis. As optional parenthesis on method calls are considered by many to be an important feature of the language, eliminating ambiguity is important. Consider:

Code Block
groovy
groovy
println (MyClass)a

or, worse:

Code Block
groovy
groovy
println (MyClass)+a

The parser is entirely at a loss with these, and will usually guess wrong. This then means rewriting of the AST by later phases, once the problem has been identified.

That said, we still can't eliminate:

Code Block
groovy
groovy
println (a)+y

but some reduction in ambiguity is better than none.

– Chris Poirier.