Skip to end of metadata
Go to start of metadata

Primary Issues

Should we attempt to Java-ize the closure syntax for Groovy? At present, we use Ruby-style parameter lists for closures:

Apart from not being very Java-ish, this policy means the opening brace for the closure must appear on the same line as the statement with which it is associated.

One suggestion (from Sam, as I recall) is to use "new" as the closure marker:

This approach has the benefit of being reasonably inoffensive to Java programmers, as it uses Java keywords and parameter style. It does, however, call attention to how closures are implemented (ie. as anonymous classes), which may or may not be a good thing.

A similar alternative is to hold more closely to Ruby:

I think this reads better, as "do" indicates the purpose of the block, whereas "new" indicates how the magic is performed. The problem here is that we would be giving "do" an expression meaning, in addition to its existing statement meaning. "new" doesn't have this problem as it is already an expression. Further, optionalities may make the use of "do" for closures ambiguous with the use of "do" for loops. That said, I think the readability of "do" is worth the effort to make it work.

– Chris Poirier

Extensions

At present, we have only one Closure to worry about. There has been some discussion, lately, of providing generators in the language. Aside from the procedural vs. declarative discussion we will have to have, there is also the question of how to present the syntax in the language.

A generator is, in some ways, a merging of a closure with an iterator – each call to the generator performs a single step in a series of operations and returns the value. As such, we will probably find it natural to use similar syntax for generators and closures, perhaps the only difference being the presence of a call to "yield()" in the body of the generator (I'm leaving the discussion of language-level backtracking support for another time). Note that this use of yeild() is very different from the use of yeild() in Ruby.

So, the question is: can we reuse closure syntax directly for generators (where "yield" is a keyword that causes the compiler to create a Generator instead of a Closure), or do we need some way for the user to specify the handling of any given block?

Possible example of block meaning selection:

– Chris Poirier

Flow Control

At present, there is no way (short of throwing an exception) to terminate a closure operation early. For instance, this does not work:

break and continue should work within closure bodies. continue is easy – it's just a jump to the end of the block. break is harder, as it has to coordinate with the caller. In Ruby, break exits the block with value "nil". The caller must check after each closure call to see if the value returned was "nil", and break itself, if necessary. A side effect is that you can't return "nil" from a block without terminating the calling structure. (As it happens, this is almost always acceptable.)

break could be implemented in two ways:

  1. the Ruby way (ie. the block return null, and the caller must check);
  2. with an exception that the caller must catch.

Option 1 is probably the better choice for performance, but does have the side effect of making null a special value. Option 2 makes it harder to forget to check for early exit.

– Chris Poirier

  • No labels

5 Comments

  1. Incidentally we can implement the factorial example right now - though the downside is that we must iterate through the entire generator first, passing in a closure that collects the values into a List - then iterate through them.

    Not quite ideal though - maybe we need a yield statement?

  2. I think closures will end up being a centerpiece of the language, allowing programmers to create really neat control flow abstractions (for searching, handling events, etc.). They will be most powerful if the simple use cases have as little "decoration" as possible, and I think Ruby and Smalltalk have got it basically right.

    Lisp's 'lambda' is, alas, too clunky, and the suggestions above with 'do' and 'new' repeat the error. I don't think we should be asking Java programmers to direct our sensibilities for a lambda syntax: Ruby and Smalltalk are better leads, because they reflect long experience with closure usage. (Note that Lisp often avoids 'lambda' by hiding it in macros, while Scheme avoids it in many cases with a 'define' syntax sugar. Nobody likes a prominent lambda.)

    Therefore, even though it is not Java-like, I greatly prefer the present syntax, as involving the absolute minimum amount of punctuation noise, as:

    Note the use of statements have values to return the result 1+x.

    For an attempt to define this syntax more explicitly, with sub-issues flagged, see open and closed blocks.

  3. RE: flow control with closures

    While we could use an exception inside the bytecode generation to denote break inside a closure, another simple solution is to return a special magic object. e.g. the implementation of this closure...

    could be

    then we add this to the Closure class

    then inside some closure method

  4. I don't like current closures syntax. | is a symbol for or operation because Groovy pretends to be superset of Java language, ideally, Groovy must be able to compile any Java program.

    Another reason is that declaration of method arguments and closure arguments should be similar to simpify programs reading and language learning. Look at Python, there function arguments both for def and lambda terminated by colon.

    I suggest simple syntax:

    a = def { println "hello" }

    or with arguments:

    a = def(what) { println "hello " + what }

    or as now

    a = { println "hello world" }

    if parameters not needed.

    We already use def for method declarations so both methods and closures parameters will be in parens and outside curly braces.

    I prefer def to new or do because closure is a function. It is not new object (no matter how it is implemented) and not code block.

  5. A closure is most definitely not a function, at least not with respect to variable scoping. And while I don't think we should call attention to the object nature of a closure (that is just an implementation detail), neither do I think we want to confuse the issue by pretending they are just functions.

    As I've said elsewhere, Groovy isn't just an extension of Java. It's a scripting language for the JVM. The JVM defines an instruction set and an object model and that's all. Because we are targetting Java developers, the path of least resistance is to take inspiration from Java's syntax and style, but that does not mean implementing the same language. Operators won't necessarily do exactly the same things, because we are taking a higher-level approach to problems.

    I would argue that a lot of our problems come from trying to keep too much of Java. Java is a very intricate language – removing typing and relaxing the syntax is to do great violence to it. And yet, those are some of the things that we want do to.

    We want to end up with a language that feels comfortable to a Java developer, but that also feels comfortable in itself.

    So, to add a concrete point to this comment, I would say that we have and/or can find better uses for those three symbols than bitwise math. We have a shortage of symbols, so keeping them free for real features is important. In a scripting language, bitwise math is not something that is so pervasive that it warrants three operators. We can do this stuff with methods, and for the occasional time that it is necessary, I think that is sufficient.