Skip to end of metadata
Go to start of metadata

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.

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:

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:

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

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:

to:

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:

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:

or, worse:

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:

but some reduction in ambiguity is better than none.

– Chris Poirier.

  • No labels

3 Comments

  1. I like it. In dynamically typed mode we'll typically not be doing any casting anyways, so 'as' is more of an explicit conversion mechanism rather than a cast.

  2. Just to be clear, while the examples didn't include typed variables, I intended that the same syntax would be used in either case.

  3. Backward compatibility with (most) Java code can be preserved essentially "for free" by defining T( x), and all if its syntactic variants, to mean that x is coerced to type T, if T is in fact a type and not some other sort of method.

    (I removed my previous suggestion about hair-splitting the fundamental method-call syntax to wedge in the Java cast syntax as a special case.)

    In particular, the primitive type boolean would call asBool in the runtime. And so forth. This takes care of Java casts "for free" without perturbing the flexibility of method calls.

    I also like x as T as a preferred alternative to (T)x or T( x).

    I have also been dreaming of a generic, programmable cast operator, and 'as' is a great name for it.

    I'll go farther, and say that the 'as' method (or a weaker related method) should be implicitly called on all assignments and method argument bindings, when the target variable has an explicit type in its declaration. See also the section Advisory Types on Declarations in classes are always objects.

    Still farther, I think the resolution of overloaded methods as multiple-dispatch methods at runtime should take into account the potential conversion paths created by various overloadings of the as method.