Skip to end of metadata
Go to start of metadata
  • No labels

10 Comments

  1. At present:

    returns a method pointer. Judging from questions posted to the mailing lists, this is seldom the expected behaviour. Assuming we keep optional parenthesis, perhaps we should provide a method on Object that returns method pointers for names, and make this syntax be an method call, too.

  2. I agree. Getting method pointers is typically done much less frequently than calling methods, so it should be more "work" to perform. Plus this will make the optional parens a little more consistent (if they stay as part of the language). OTOH the current syntax seems more consistent: since "MyClass" by itself is a Class object, then "method" by itself is a method object.

    BTW how does the existing code work when "method" is polymorphic and has several different versions?

  3. The most common meanings for object.method are:

    1. Calling a method with no arguments.
    2. Fetching a field or property.
    3. Storing a field or property.

    The less common meanings are:

    1. Getting a reflective descriptor.
    2. Getting a method pointer (callable closure over the object and method).
    3. Building some sort of path expression.

    The common meanings are disambiguated by lookahead context in Java. (And there are no properties.) Groovy can be more concise than Java in the common cases if it allows a little more ambiguity, specifically by eliding empty argument lists. This significantly shortens the first case above.

    The less-common meanings require more or less awful boilerplate code in Java, which we may or may not care about. Personally, I think class literals are nice and would like to see concise field and method literals also. Depending on what the method pointers are ready to interoperate with (a la Delphi/C#), a concise method closure syntax might also be good.

    I suggest a simple syntax to fetch (but not "execute") a combined "lvalue descriptor", such as object.&field. This cookie could be queried programmatically as to whether it names fields, properties, functions, and/or overloaded methods. It would have the obvious "call", "get", and "set" operations, for use as a method-pointer or property-pointer.

    I am consciously confusing fields with methods here: I think Groovy will be simpler if we allow fields to mix with methods in most places.

    Then, we can express the common cases concisely:

    Use Case

    Semantic Expansion

    x = object.value

    x = object.&value.get()

    object.property = x

    object.&property.set( x )

    object.method(x,y)

    object.&method.call(x, y)

    Of course, the compiler would short-circuit or fold away all the common cases of this.

    BTW, eliding parenthesis and commas around long argument lists is not very profitable, even though it's handy in the shells. The reason is simple: The user is already typing a lot of stuff to introduce arguments, so the burden of an extra (,,) decoration is small. So, issue 15205 quoted above can go away either requiring parentheses around arguments, or perhaps by requiring them if a closure block is to follow.

    See design tactics for an attempt to list relevant design principles that affect this discussion.

  4. Let me clarify my last BTW by showing what I think is the sweet spot for eliding parenthesis. Basically, we can be shell-like and omit parentheses with zero or one arguments, but with two or more arguments, and especially with a block appended, the parentheses are necessary to keep humans and parsers from straying down syntactic garden paths.

    Example

    Arguments

    Comment

    println

    ()

    OK, omit useless ()

    println 'hi'

    ('hi')

    OK, omit annoying ()

    atan2 3,4

    (3,4)

    Probably overkill. Disallow?

    atan2 3 4

    (3,4)

    Badly ambiguous

    using x {code}

    (x,{code})

    Mortally ambiguous

    if x>0 {code}

    (x>0,{code})

    Mortally ambiguous

    I once modified a Java frontend to supply elided parentheses around 'if' and 'while' expressions, but found that the expressions were difficult to keep syntactically separate from the body statement, and the paren-elided code was hard to read.

    My basic point is that punctuation elision is a good thing, but only for use cases which are already quite small and deserve to get even smaller.

  5. Another example of why optional parenthesis are evil (posted by Troy Heninger on the groovy-user list):
    Using Jad I discovered that Groovy is compiling the following:

    To be a this.getProperty("or") and then calling its getAt(...) method,
    passing rule1 and rule2 as parameters. Adding parens solved my problem. I
    wanted, instead, to call the 'or' method and pass a List as a single
    parameter.

  6. Allowing "f x" as a synonym for "f( x )" inside arbitrary expressions is asking for trouble, as the previous comment shows.

    The benefit of shell-like call syntax applies only to statements, not to arbitrary subexpressions.

    It is reasonable to allow statements which look liike "println x" but not expressions which look like "2+(atan2 x, y)". (I regret the choice of atan2 in the examples above.)

  7. This was discussed in the groovy-user list at the end of April ("Parens and Parameters"). I initially agreed that optional parenthesis were nice in limited situations like:

    But, as I discovered, even this example can be ambiguous. Consider the following:

    It "obviously" gets a NullPointerException.

    Chris Poirier pointed out that the parser could consider white space to deal with this. However, even if the parser can handle it, I think most people would be confused by it. Consider the following:

    If white space were used to remove the parser ambiguity, these two statements would have completely different meaning. The first one would print 7.3, the second one would get a NullPointerException.

    To avoid parenthesis in the println example, as James pointed out, you can do the following instead:

    In the end, while I initially liked optional parenthesis for simple statements, I think they still cause too much ambiguity.

    One exception to this that I agree with is for passing closers into a method (as James pointed out in the thread). Examples provided by James:

  8. If an identifier is immediately followed by a left parenthesis, a function is being called, in lots of languages. The left paren, when it follows an identifier, tightly binds to that identifier.

    I do not see how "println (foo)+2" is any more misleading than "3 * foo+2". In both cases, whitespace can be abused to mislead the reader. In both cases, the reader has to know something about the order of operations (syntactic binding strength of operators) to avoid being misled.

    If you find yourself starting to write "println (foo)+2", then add more parens to sort out the grouping: "println ((foo)+2)". Let's not get startled by a little bit of ambiguity: It comes with the territory, especially when our goal is terseness. Brevity entails ambiguity.

  9. It's not the whitespace which introduces the ambiguity. It's that the parenthesis are optional and can be interpreted as either the method delimiters or the expression grouping. Whitespace just exacerbates the issue.

    The reason "println (7.3F).doubleValue()" is more misleading than "3 * foo+2" is that numeric precedence is a clearly established practice, while method delimiting vs. expression grouping is not.

    I'm not sure I'm willing to concede that our main "goal is terseness". I think the goal is an intuitive, powerful and reasonably concise language. I don't think a small gain in brevity can be used to justify a loss of intuitive syntax. And saying that it is optional does not solve the problem. In fact, I think it makes the problem worse. The language should not invite ambiguity and a million ways to write the same meaning (i.e. Perl).