Hi Markus,
Thanks for your suggestions. Some neat ideas in your suggestions! Just
a general comment to start off with...
With language design, it's often the details which are important, so
I'd suggest rather than going straight from this email to a bunch of
PRs, you'd create separate Jira issues for those items you want to
pursue and flesh out the design a little more - it's often the dealing
with backwards compatibility and handling error situations that take
up more of the design time than the actual feature itself. Having said
that, please don't be put off with the extra work. We usually follow
the same process ourselves and find the extra discipline helps all
round. For simple non-controversial enhancements, not much detail
might be needed but in other cases, there might be lots of details
that need to be handled that might not be apparent at first - plus a
Jira issue is a good place for us to suggest hints on how to get started.
Some more comments inline below ...
Cheers, Paul.
On Mon, Aug 21, 2017 at 6:25 AM, MG <mg...@arscreat.com
<mailto:mg...@arscreat.com>> wrote:
Hi,
before I create change requests, I wanted to ask for some quick
feedback on the following potential new Groovy features (I
apologize beforehand, if any of these are already covered in some
way, and I missed it, or if they have been discussed before, etc -
just throwing ideas that I find useful out there :-) ):
1. Named Parameters: I already posted that feature request here.
The most plain vanilla support (support the <name>:<value>,
<name>:<value>, ... syntax to give named ctor/method args)
would already cover 99% of applications here, I think. The
imho most problematic feedback was external libraries without
debug information - my question would be how frequently such
libraries occur in practice (I personally have never pressed
Ctrl+B in IntelliJ and not gotten a method decompile result
with parameter names) ?
It is certainly worth fleshing this out a little more. Such support
has been considered in the past [1,2] but that was prior to
JEP118[3,4] being finalized and we decided to wait. Some of the
backwards compatibility issues mentioned in that thread still remain
and we'd need to spell out what we are planning in that regard (that
affects what version this might be merged into). I'll also note that
there are some tricks that IDEs might use (looking at source code,
javadoc etc.) that we would not likely pursue. There are also some pre
java8 tricks that can be used [5,6,7] but I think we would more likely
want to pursue the JEP118 approach - though do feel free to experiment
if you think those approaches might be useful - we'd need to weigh up
what additional libraries that might required to be available for
basic compilation. I have less optimism about whether JEP118 metadata
information will be available anytime soon - doesn't mean we shouldn't
provide support in the meantime for the situations where it does
exist. I did a sample size of 1 by downloading the latest Guava and it
wasn't available in that library - also the Java libraries themselves
didn't have that information last time I checked. Also, there is an
old issue that could be useful that would work for constructors even
on old JDK versions using a JavaBean annotation[8] though again I
don't think widely used. Also, we'd need to understand if there are
any impacts on Groovy's mixed Map/positional notation and whether true
optional arguments support would be included[9].
[1] https://groups.google.com/forum/#!topic/groovy-user/8MuO1TTw-aQ
<https://groups.google.com/forum/#%21topic/groovy-user/8MuO1TTw-aQ>
[2] https://github.com/pniederw/groovy-extensions
<https://github.com/pniederw/groovy-extensions>
[3] http://openjdk.java.net/jeps/118 <http://openjdk.java.net/jeps/118>
[4] https://bugs.openjdk.java.net/secure/attachment/11079/8misc.pdf
<https://bugs.openjdk.java.net/secure/attachment/11079/8misc.pdf>
[5] https://github.com/paul-hammant/paranamer
<https://github.com/paul-hammant/paranamer>
[6]
http://docs.spring.io/spring/docs/3.0.x/javadoc-api/org/springframework/core/LocalVariableTableParameterNameDiscoverer.html
<http://docs.spring.io/spring/docs/3.0.x/javadoc-api/org/springframework/core/LocalVariableTableParameterNameDiscoverer.html>
[7]
http://docs.spring.io/spring/docs/3.0.x/javadoc-api/org/springframework/core/ParameterNameDiscoverer.html
<http://docs.spring.io/spring/docs/3.0.x/javadoc-api/org/springframework/core/ParameterNameDiscoverer.html>
[8] https://issues.apache.org/jira/browse/GROOVY-3661
<https://issues.apache.org/jira/browse/GROOVY-3661>
[9] https://issues.apache.org/jira/browse/GROOVY-3520
<https://issues.apache.org/jira/browse/GROOVY-3520>
1. Support making all method/ctor parameters final by default
through "autofinal" Groovy compiler flag:
Foo(int x, String s) { // final keyword is auto added to all
parameters, if compiler flag is set, i.e. this automatically
becomes Foo(final int x, final String s)
this.x = x; this.s
}
Rationale: Even if Groovy source samples use def instead of
final a lot, parameters (and variables) are, according to my
experience, most of the time actually final in practice (In
the few cases where one needs to modify a parameter, it can
immediately be assigned to a variable). This feature would
reduce the source code clutter that comes from the need to
qualify parameters as final all the time.
You can already leave off the "clutter" and apply a CodeNarc rule[1]
to detect "bad" style. Also, it would be an easy task to create an
@AutoFinal local transform which could then be automatically applied
using a compiler customizer. I think going further with a dedicated
compiler flag would be a later step if such a local transform became
extremely popular.
[1]
http://codenarc.sourceforge.net/codenarc-rules-convention.html#ParameterReassignment
<http://codenarc.sourceforge.net/codenarc-rules-convention.html#ParameterReassignment>
1. Deduce the type of final fields from their assigned value:
2. class Foo {
final device = new PrinterDevice(...) // device field will
have type PrinterDevice instead of Object when reflection is
used on class Foo
}
Rationale: While IntelliJ does a good job at deducing the type
of final fields, it would still be better if the Groovy
language itself would use the more specialized type here, for
e.g. reflection purposes
With @Typechecked or @CompileStatic type inferencing is going to be in
play. During debugging the runtime type is going to be available. What
"reflective purposes" did you have in mind?
1. Introduce a "var" (o.s.) keyword that allows deduction of type
through assignment:
var device = new PrinterDevice(...) // device variable will
have type PrinterDevice without the need to explictely state that
Rationale: This is a well known feature of other languages,
that reduces the need to explictely define the type of variables.
How is this different to the current type inferencing?
1. Always allow ctor calls without new keyword:
final foo = Foo("abc") // creates a new Foo instance by
calling Foo class ctor with signature
Rationale: new keyword was necessary in C++ to distinguish
heap vs stack variable allocation. No such need exists in
Groovy, so the keyword just clutters the source code. Naming
convention of method names being always lowercase prevents
name clashes with methods.
A partial solution to this is available via @Newify. As far as I
know, @Newify hasn't been super popular, so we'd need to think
carefully about introducing this.
1. "Variable plus Name" support: One of the most useful macros I
used in C++ was NV(x), where I passed a variable, and created
a NamedVariable instance, which was automatically passed the
name and the value of the variable (through simple macro
stringify). I would love to see something similar in Groovy.
Rationale: Most important application is DRY creation of debug
output, e.g.: println NV(longVariableName) // equivalent
result to println "longVariableName=$longVariableName" avoding
the potential classical copy & paste error
"longVariableName=$longrVariableName"
Sounds like a possible candidate for using Macros?
1. Support break/continue to work as if a closure was a block
construct, e.g. through an annotation on the Closure parameter:
sqe.forEachRow("select * from PERSON") { final row ->
if(row.TAG == 0) { continue } // Move to next iteration in
forEachRow iteration over PERSON table
if(row.TAG == 1) { break } // Return form closure & return
from forEachRow (effectively stopping to iterate over PERSON
table rows, continuing after the method call)
}
Rationale: Groovy's support for giving a Closure as the last
argument outside of the parameter brackets is one of its most
neat features. Supporting break/continue with expected (least
surprise) semantics inside of such closures, would make this
feature complete.
(Note: I have implemented support for a "BreakLoopException"
in my code, so that the closure body can signal a break to
e.g. forEachRow, but that is still quite an akward solution,
that breaks the illusion that forEachRow works like a regular
for-each statement)
Non-local transfers (break/continue/non-local returns) have been
discussed before[1] but perhaps worth revisiting now that Scala
supports the non-local return approach using Exceptions[2] and Kotlin
has labelled returns in lambda functions[3]. The main issue to keep in
mind would be backwards compatibility/breaking changes.
[1]
http://blackdragsview.blogspot.com.au/2006/09/non-local-transfers-in-groovy-90.html
<http://blackdragsview.blogspot.com.au/2006/09/non-local-transfers-in-groovy-90.html>
[2] http://dev.bizo.com/2010/01/scala-supports-non-local-returns.html
<http://dev.bizo.com/2010/01/scala-supports-non-local-returns.html>
[3] https://kotlinlang.org/docs/reference/returns.html
<https://kotlinlang.org/docs/reference/returns.html>
1. CompileStatic byte code call compatibility with Java: I have
included this, since I saw it on the mailing list here, and I
always tell everyone that Groovy is basically a drop in
replacement for Java, which in this case, if obfuscation is
part of the game, does not seem to be the case (in addition my
son has also started to mod Minecraft a bit, and I wanted to
use this to introduce him to Groovy - after he has suffered
through a bit of Java ;-) ).
There are definitely edge cases where @CompileStatic falls back to
dynamic calls which we want to improve.
mg