jastrachan | 13 Oct 07:42 2004

Re: [groovy-dev] making the bytecode generation more understandable

On 9 Oct 2004, at 06:33, John Rose wrote:
> On Oct 8, 2004, at 4:48, Neil Ellis wrote:
>> ...Groovy will get is an optimization on the static cases but will
>> fundamentally have to treat objects as being dynamic (i.e. it can 
>> never
>> complain that a does not have a method b). This may seem like
>> nitpicking, but it is a fundamental difference. I.e. if I type
>> "".eqwals("") the compiler will not complain and it will become a
>> runtime error which to me is not static typing.
> Neil, earlier you wrote that you want a "strongly typed safe, 
> performant" language.
> "Safe": Groovy, like anything built on the JVM, will have a certain 
> level of safety.
> "Performant": With the optimizations James is thinking of, this should 
> be possible.
> (There has to be a weak sort of sealing rule which says that the 
> runtime cannot contradict
> observations about class structure made at compile time.  This may be 
> reasonable for
> Java fields and methods, but might not for other kinds of Groovy 
> members.
> This requires study, I think.)
> That's two out of three; not bad.  By "strong typing" you mean (quite 
> reasonably,
> though it wasn't clear to me at first) specifically the error checking 
> of member
> references.  As you point out, this is not in Groovy.  In order to put 
> it into
> Groovy, we would need a sealing convention strong enough to state at
> compile time that a type is totally non-extensible beyond what is 
> visible
> at compile time.  Such a sealing convention might possibly emerge from
> our work refining the scoping rules, and the way they interact with 
> static
> typing, but (I think) it will require extra care to make this happen.
> Here's an example of different kinds of typing.
>     File f = new File("foo")
>     def x = f
>     Object o = f
>     f.getName()   // OK, statically bound call to File.getName
>     x.getName()   // duck-typing says OK, calls File.getName more 
> slowly
>     o.getName()   // Neal wants a compile error on this one!

Agreed - those would be my thoughts too, since the developer explicitly 
wanted to constrain o to be of type Object. The only real value of 
static typing (apart from the performance boost) is (i) IDE metadata 
for name completion and (ii) to generate compile errors if you make a 
typo. It seems if we've got the metadata available at compile time, we 
might as well generate compile errors.

> Neal's compile error would occur (I suppose) because the compiler 
> would note the following facts:
>   1. Object is the type of o
>   2. Object's compile-time type has no getName method
>   3. no Groovy method called getName is imputed to Object (at compile 
> time)
>   4. there is no "runtime invoke" hook on Object (either actual in 
> Java or imputed via G. M.)
> By the "imputed" stuff I mean what is found in the bunch of 
> definitions in DefaultGroovyMethods.
> By runtime invoke hook I mean the usual InvokerHelper machinery 
> interface, which is currently
> (I think?) made applicable to all Groovy object references, regardless 
> of type.


If a type is statically constrained, we could use regular static method 
dispatch in the bytecode and so avoid using the slower, dynamic 
invocation. But thats purely an optimisation.

> To make these steps work we would need 1. compile-time types, 2/3. 
> compile-time models
> of types, and 4. most plain Java compile time types not to use the 
> invoker.
> Types subject to 4 would compile correctly only in a Groovy 
> compile-time environment
> which was fully "up to speed", with all the latest imputed Groovy 
> methods.
> I suppose that's an OK rule.
> This seems like a lot of structure, but if we are needing some of it 
> for other purposes
> (like performance) maybe it's reasonable.
> As a reasonable alternative, we could punt on the performance helps 
> and the static typing,
> and adopt a simpler "always dynamic" understanding of Groovy member 
> references.
> In this understanding, the type of a variable doesn't affect the 
> compilation of its member
> references.
> Instead, the variable type simply makes an assertion about the types 
> of its values.
> The key impact of such an assertion would be a type test on every 
> assignment to
> the variable, causing an exception if somebody tried to put the wrong 
> type in.
> There's also an effect of typed method arguments on method calls, of 
> course.
> The maxim "Groovy is a dynamic language" can't always guide us.  It 
> doesn't
> help with the scoping of local names, for example.  (We don't want to 
> repeat
> the early design errors of Lisp with dynamic scoping, errors which 
> Smalltalk
> avoided.)  But maybe it is the right answer for member references.
> As we refine and clarify the scoping rules of Groovy, we'll be drawing 
> the line
> more clearly between static and dynamic behaviors.  I'm curious which 
> of the
> two models for member references will turn out to be the right balance 
> of clarity
> and power.