jastrachan | 25 Jan 14:19 2005
Picon

Re: [groovy-dev] Pragmatic Groovy Development and Project Management

On 25 Jan 2005, at 12:46, John Wilson wrote:
> On 25 Jan 2005, at 11:45, jastrachan@... wrote:
>
>>>
>>> The only thing I have seen which purports to replace the delegate is 
>>> the with clause which does no such thing.
>>
>> We've not yet proposed a replacement builder mechanism - though we 
>> briefly discussed at the meeting a few possible alternatives.
>>
>>> The delegate does not lead to complete dynamic scope at all. The 
>>> delegate only comes into play if the static scope resolution fails 
>>> to resolve the name.
>>>  You can always tell at compile time if the object is a static or 
>>> dynamic reference. The dynamic references can either be against the 
>>> owner (if the owner is a script or declared in a script, if it is 
>>> another closure or if it is a class which implements get/set 
>>> Property) or against the delegate. Removing the delegate still gives 
>>> you dynamic name resolution if the owner is dynamic. So removing the 
>>> delegate does not decrease the complexity of name resolution within 
>>> closures to any appreciable degree.
>>
>> A hack is still a hack, even if its useful :)
>
> However it does not cause the problem you claim it causes. So what 
> problem does it cause?

Stepping back. The 'delegate' has no place on a closure - a closure is 
meant to just be a bit of code tied to its lexical scope. The delegate 
is a horrible hack to do builders which leads to all sorts of naming 
issues & complex runtime name chasing in many different scopes (is a 
name a global function, a method on the parent, or on this new magic 
delegate thingy etc).

Even when the delegate plumbing goes away, you can mimick it if you 
really need it...

delegate = foo

closureWithDelegate = { delegate.somethingRandom() }

delegate = bar

closureWithDelegate.call()

its just now its explicit, that the 'somethingRandom' is associated 
with the 'delegate' object, not some complex trawl around every 
possible object in scope to figure out where it comes from.

>> Like I said, we will have a builder mechanism that allows you to do 
>> builder type stuff.
>
> But nobody knows what it will be.

By all means propose a builder syntax if you like - we need to discus & 
decide one.

Our thoughts in the JSR meeting were something like

with(foo) {
     bar()
     xyz(123)
}

which logically means

foo.bar()
foo.xyz(123)

or if the foo implements some magical builder interface, results in 
calls something like

foo.build("bar")
foo.build("xyz", 123)

>>>>> 2/ The introduction of optional parentheses for methods taking no 
>>>>> parameters makes it impossible for the compiler to distinguish 
>>>>> between a method invocation and a property access on a untyped 
>>>>> value. This seems to me to require a complete rethink of how GPath 
>>>>> works.
>>>>
>>>> We need to clarify this. The tests in the TCK right now assume that 
>>>> parens are required to disambiguate between a property and a 
>>>> method. i.e.
>>>>
>>>> collection.size
>>>>
>>>> would work, but if you had a size() method and a size property, 
>>>> you'd have to use
>>>>
>>>> collection.size()
>>>>
>>>> to explicitly use the method
>>>>
>>>
>>> Yes but when you have untyped variables or dynamic behaviour the 
>>> compiler can't tell what collection.size is. So we have a semantic 
>>> difference in the interpretation of x.size depending on whether x is 
>>> typed or not.
>>
>> No - its nothing to do with typed/untyped. Whatever the type of 
>> collection, the expression has the same AST & will use the same 
>> resolution mechanism. But I think size() has to have a different AST 
>> to size to allow disambiguation between bean properties and methods 
>> with zero args. We should discuss this on the JSR list - there's a 
>> test in the TCK for this, we might wanna refine the semantics though
>
> Let me give you a concrete example:
>
> X is a class which has a public method int size()
>
> Y is a subclass of X with a public property called size of type int
>
> for (X a in [new X(), new Y()] {
> 	r = a.size
> }
>
> presumably the size method is called both times round the loop?

No, It would evaluate the 'size' property on both types, its up to the 
type to decide on how to implement that via its MetaClass. If a class 
has no 'size' property, it'd default to the size() method if applicable 
or produce an error. We can check with the MetaClass at compile time 
and generate compile time warnings if using static typing to catch more 
user errors.

> def b = new Y()
>
> r = b.size
>
> presumably the size property is accessed?

Yes

> If the answer to the first question is no then the runtime has to do 
> refection magic to determine what to do even though a is statically 
> typed.

Yes, we still use the MetaClass to decide what methods/properties are 
available. If the MetaClass decides, it could punt and defer until 
runtime (e.g. if the class implements some magic dynamic interface to 
do things like dynamic property resolution, or dynamic method proxying 
or something).

> This seems to contradict your statements on statically dispatching 
> method calls on statically typed variables.

No it doesn't.

>>
>>>>> 3/ Various Perl like variable decorators are being introduced in 
>>>>> an attempt to disambiguate cases introduced by the language 
>>>>> changes.
>>>>
>>>> Huh?
>>>>
>>>
>>> Use of  <at>  as a operator/decorator (
>>
>> We're adding support for annotations via  <at>  to be inline with Java 5 - 
>> we're also allowing explicit references to attributes (fields) via 
>>  <at> foo - though most people won't need this as most people will be 
>> using bean properties etc. FWIW this comes from Ruby, but I'm sure 
>> there's something like it in Perl as they tend to use all possible 
>> operators :)
>
> you have added  <at>  and * operators (or, at least that's what the syntax 
> says).  The  <at>  operator seems to be reused in the with clause to mean 
> "not with". You're on a very slippery slope here.

Why?

Note that the EBNF isn't fixed in stone and one of the reasons for its 
existence is to document stuff and prompt discussion on the JSR list.

>>> and the horrid use of * as an array expander)
>>
>> * is used in python and ruby (and quite a few languages) to allow 
>> lists and parameter values to be used together.
>>
>> def foo(a, b, c) {
>>     ...
>> }
>>
>> list = [1, 2]
>>
>> foo(*list, 3)
>>
>> Its unfortunate you find this horrible; its very handy. Also note 
>> Java 5 has the converse end of the relationship, allowing a single 
>> parameter to be a list of arguments via "...". It just Java 5 doesn't 
>> allow the converse - to expand an array/list to be the arguments of a 
>> method call.
>
> Python does not have a '*' operator. It uses * and ** to denote 
> variable parameter list and named parameter lists. I really don't 
> agree that "it's really handy". I don't think I have ever seen 
> foo(list[0], list[1], 3) written in any programming language. It's 
> just not an idiom I recognise.

Read the language specs closer then :). Both Python and Ruby have a 
similar * operator

http://python.org/doc/2.4/ref/calls.html
http://www.rubycentral.com/book/tut_methods.html

Its pretty standard stuff

> The operator looks to be a solution looking for a problem.
>
> I really think operators have to earn their place in a language rather 
> then being put in in case they are useful to somebody at some time.

Agreed.

>>
>>>>> In addition a breaking change has already been made to the way 
>>>>> dynamic method dispatch is applied to untyped variables (e.g. 
>>>>> toString cannot now be processed by invokemathod())
>>>>
>>>> What was this change?
>>>
>>> http://jira.codehaus.org/browse/GROOVY-677
>>>
>>> You will note that this issue was opened and closed in a single day 
>>> and there was no discussion whatsoever of the wisdom of this change 
>>> on any of the Groovy mailing lists.
>>
>> We need to support static compile time errors. Groovy Classic has 
>> some broken stuff in there in this area. I'm confident we can fix it. 
>> However in the mean time we're gonna have to change a little some of 
>> the hooks that folks like yourself have used (which were only ever 
>> introduced into Groovy to help the implementation) and provide a more 
>> explicit way for you to do the wacky dynamic proxy/dispatch stuff you 
>> wanna do. Believe me, I've always tried to keep things backwards 
>> compatible where possible, but the closure delegate & current use of 
>> invokeMethod() overloading to add dynamic behaviour will have to 
>> change. I'm sure we'll be include the same kinda stuff you wanna do - 
>> it'll just differ a little & rather than being a hack in the 
>> implementation, we'll layer it neatly on a solid base which allows 
>> both static compile errors and dynamic method dispatch.
>
> James this is all very well but it brings is to the real point of the 
> discussion.
>
> People are being given the impression that New Groovy is just a kind 
> of cleaned up version of Old Groovy. that there will be minor changes 
> but they will not really have much of an impact. It's really a kind of 
> rolled up bugfix. Unfortunately this is just not true. New Groovy has 
> fundamental breaking changes to the semantics of Old Groovy.

What semantics are breaking?  Apart from the delegate stuff, which few 
people other than you are using, what else is breaking? Sure we're 
temporarily putting the builder stuff to one side until we've the core 
of New Groovy on a solid, stable base, then we're adding something back 
in its place.

>  I would estimate that 80% of all the Groovy code I have ever written 
> will be broken by New Groovy. Now we can have perfectly reasonable 
> discussions about whether this is a good or bad things, but we can't 
> escape the fact that New Groovy is substantially different to Old 
> Groovy. I think that the members of the Groovy community are entitled 
> to be made aware of this fact and I don't think that they have really 
> grasped the nature and extent of the changes up to now.

80% come on John. Hardly anyone but you uses the delegate mechanism 
thats hidden inside Closure. Sure some stuff will break - and we'll 
find out how much by putting all the old Groovy test cases through the 
Groovy New parser, but this magical 80% figure is massively pessimistic 
and not realistic to at least any of the groovy I write.

I also think you're assuming that once we remove some of the old cruft 
in Classic Groovy we're gonna stop right there and not add anything 
else into the language to do the more dynamic/template/builder based 
stuff you like to do - the aim is to simplify, get a stable core 
without any of the hacks or crufts, then add stuff we really need on 
top - only if we really need them.

James
-------
http://radio.weblogs.com/0112098/


Gmane