Quantcast

Named arguments and maps

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Named arguments and maps

Wujek Srujek
Hi. The way groovy currently implements named arguments is that all of them are gathered together in a map, which is always the first parameter passed to the method / function. Groovy in Action (version 1) says it is a workaround for the fact, that the JVM doesn't support storing parameter names in the bytecode.
Well, it has changed with Java 5, although not directly - parameters can be annotated, the annotations can store the names, and with runtime retention the groovy runtime could ask for a list of argument names. Here is some Groovy code that shows the idea, with a few comments:

import java.lang.reflect.*
import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME)
@interface Named {
    String value()
}

class Foo {
    // the bytecode generated by the backend always annotated parameters with their names, so this is always available
    def method(@Named('a') int a, @Named('b') int b) {
        println a + b
    }
}

def names = Foo.getMethod('method', int, int).getParameterAnnotations().flatten().grep(Annotation).collect { it.value() }
assert names == ['a', 'b']

(Of course, the code would probably be in Java, but this example is much more expressive in Groovy. Also, the more I write Groovy, the less I feel like doing Java anymore ;d)

The idea is that the compiler backend that you guys have knows the parameter names (via the AST it works with), and annotates _each_ parameter with @Named(name) when it generates the bytecode. Then, the runtime dispatch mechanism retrieves the list of parameter names and uses it to correctly feed the method / function, reporting errors when there is a typo / an unknown named parameter is used.
This could be much safer than the map approach now - currently, the maps accept just anything. Moreover, the developers are now forced to iterate over the map to initialize all params that were not specified / check if they were specified, whereas this new approach would do it automatically. It would work kind of the same way the default map-based constructor does, when it iterates over the keys and checks if there is a field with the same name, failing when some name is unknown.
Could this approach somehow be incorporated into Groovy, so that we get real named arguments, with the runtime asserting the validity for us? I expect this to be pretty tough due to backwards compatibility, but I guess Groovy 2.0 is open for such changes? Also, I am not sure about builders and other code that relies on the flexibility to use maps, so maybe the syntax could be extended, like:
method(a: 1, b:2)
being what it is now - loose and flexible, and
method(a=1, b=2)
using the new, strict semantics, with the checks and so on?

I am just thinking loosely, as I am a Python user and the second syntax is supported there, and even suggested in certain use cases. Sometimes the map approach is too 'loose', having to do the checks by myself and so on. Maybe this could be changed or at least considered?

wujek
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Named arguments and maps

Jochen Theodorou
Am 22.12.2011 14:28, schrieb Wujek Srujek:
[...]

> import java.lang.reflect.*
> import java.lang.annotation.*
>
> @Retention(RetentionPolicy.RUNTIME)
> @interface Named {
>      String value()
> }
>
> class Foo {
>      // the bytecode generated by the backend always annotated
> parameters with their names, so this is always available
>      def method(@Named('a') int a, @Named('b') int b) {
>          println a + b
>      }
> }
 >
> def names = Foo.getMethod('method', int,
> int).getParameterAnnotations().flatten().grep(Annotation).collect {
> it.value() }
> assert names == ['a', 'b']
>
[...]
> The idea is that the compiler backend that you guys have knows the
> parameter names (via the AST it works with), and annotates _each_
> parameter with @Named(name) when it generates the bytecode. Then, the
> runtime dispatch mechanism retrieves the list of parameter names and
> uses it to correctly feed the method / function, reporting errors when
> there is a typo / an unknown named parameter is used.

that will make the method selection much more difficult. That is no
problem we cannot solve of course. But there is one usage that doesn't
fit what you have said so far... for example:

def foo(Map m) {
   bar(m)
}

in this case foo may not know all the parameters of bar. With your
requirement for reporting errors, this can no longer work. Also the Map
collecting first parameter would be replaced I guess, but there is no
"collect all (surplus) entries" in your suggestion. That means the call
to bar works only if bar is using a map. Trying to keep the map style
invocation provokes some problems. For example in

def bar(Map m){...}
bar(m:1)

Is this now supposed to call bar with the Map [m:1], or is it supposed
to throw an error, because m should be a Map and no int?


> This could be much safer than the map approach now - currently, the maps
> accept just anything. Moreover, the developers are now forced to iterate
> over the map to initialize all params that were not specified / check if
> they were specified, whereas this new approach would do it
> automatically.

that's true.

> It would work kind of the same way the default map-based
> constructor does, when it iterates over the keys and checks if there is
> a field with the same name, failing when some name is unknown.

not failing right away, first we need to check the other methods of the
same name if they they are there.

> Could this approach somehow be incorporated into Groovy, so that we get
> real named arguments, with the runtime asserting the validity for us? I
> expect this to be pretty tough due to backwards compatibility, but I
> guess Groovy 2.0 is open for such changes?

We decided to change the version scheme, 1.9 will be 2.0 and the next
major version will then be not 2.1, but 3.0. I think for the new 2.0 it
is too late, since we want to release early next year. So earliest
possible point would be 3.0. But basically it is open for that, yes. But
as I pointed out above we have to discuss this in terms of backwards
compatibility a bit more.

> Also, I am not sure about
> builders and other code that relies on the flexibility to use maps, so
> maybe the syntax could be extended, like:
> method(a: 1, b:2)
> being what it is now - loose and flexible, and
> method(a=1, b=2)
> using the new, strict semantics, with the checks and so on?

I would like to hear others about this one.

> I am just thinking loosely, as I am a Python user and the second syntax
> is supported there, and even suggested in certain use cases. Sometimes
> the map approach is too 'loose', having to do the checks by myself and
> so on. Maybe this could be changed or at least considered?

We normally always at least consider the changes ;)

bye blackdrag

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Named arguments and maps

Wujek Srujek
Hi Jochen,

of course I am aware that the idea is not well thought through at this point, just wanted to start a discussion. I lack the expertise that you have, and have no knowledge of the internals, so please be gentle ;d
As for the issues you mentioned, I am not sure, but if Groovy 3.0 allowed the new syntax (bar(a=1)) to mean the strict behavior, the new functionality would be backwards compatible? One would use the 'strict' syntax only when one is sure what parameters there are, and the current 'loose' syntax could be used as well.  Naturally, people might dislike the idea of new syntax.. The thing is, I am not that experienced in Groovy (I have been using it for about 2 months, wrote an internal DSL for works great and have become a great fan in the process), but I always wanted this strict behavior, and found the fact that I have to check the Map / initialize it in the method itself pretty annoying (as compared to the map constructor and other Groovy stuff; it is nothing even close to Java-annoying). If the idea is dismissed as nonsense, I can live with that, but it was worth a shot ;d

wujek

On Fri, Dec 23, 2011 at 12:23 PM, Jochen Theodorou <[hidden email]> wrote:
Am 22.12.2011 14:28, schrieb Wujek Srujek:
[...]

import java.lang.reflect.*
import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME)
@interface Named {
    String value()
}

class Foo {
    // the bytecode generated by the backend always annotated
parameters with their names, so this is always available
    def method(@Named('a') int a, @Named('b') int b) {
        println a + b
    }
}
>
def names = Foo.getMethod('method', int,
int).getParameterAnnotations().flatten().grep(Annotation).collect {
it.value() }
assert names == ['a', 'b']

[...]

The idea is that the compiler backend that you guys have knows the
parameter names (via the AST it works with), and annotates _each_
parameter with @Named(name) when it generates the bytecode. Then, the
runtime dispatch mechanism retrieves the list of parameter names and
uses it to correctly feed the method / function, reporting errors when
there is a typo / an unknown named parameter is used.

that will make the method selection much more difficult. That is no problem we cannot solve of course. But there is one usage that doesn't fit what you have said so far... for example:

def foo(Map m) {
 bar(m)
}

in this case foo may not know all the parameters of bar. With your requirement for reporting errors, this can no longer work. Also the Map collecting first parameter would be replaced I guess, but there is no "collect all (surplus) entries" in your suggestion. That means the call to bar works only if bar is using a map. Trying to keep the map style invocation provokes some problems. For example in

def bar(Map m){...}
bar(m:1)

Is this now supposed to call bar with the Map [m:1], or is it supposed to throw an error, because m should be a Map and no int?



This could be much safer than the map approach now - currently, the maps
accept just anything. Moreover, the developers are now forced to iterate
over the map to initialize all params that were not specified / check if
they were specified, whereas this new approach would do it
automatically.

that's true.


It would work kind of the same way the default map-based
constructor does, when it iterates over the keys and checks if there is
a field with the same name, failing when some name is unknown.

not failing right away, first we need to check the other methods of the same name if they they are there.


Could this approach somehow be incorporated into Groovy, so that we get
real named arguments, with the runtime asserting the validity for us? I
expect this to be pretty tough due to backwards compatibility, but I
guess Groovy 2.0 is open for such changes?

We decided to change the version scheme, 1.9 will be 2.0 and the next major version will then be not 2.1, but 3.0. I think for the new 2.0 it is too late, since we want to release early next year. So earliest possible point would be 3.0. But basically it is open for that, yes. But as I pointed out above we have to discuss this in terms of backwards compatibility a bit more.


Also, I am not sure about
builders and other code that relies on the flexibility to use maps, so
maybe the syntax could be extended, like:
method(a: 1, b:2)
being what it is now - loose and flexible, and
method(a=1, b=2)
using the new, strict semantics, with the checks and so on?

I would like to hear others about this one.


I am just thinking loosely, as I am a Python user and the second syntax
is supported there, and even suggested in certain use cases. Sometimes
the map approach is too 'loose', having to do the checks by myself and
so on. Maybe this could be changed or at least considered?

We normally always at least consider the changes ;)

bye blackdrag

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

  http://xircles.codehaus.org/manage_email



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Named arguments and maps

Guillaume Laforge-4
It's definitely not an idea to be dismissed as nonsense, as we'd love to have "real" named arguments working.

But we obviously have some concerns, regarding speed of method selection, whether JDK 9 might provide some more support here (for example encoding parameter names in the bytecode), and obviously, backward compatibility issues, and so on.

So it's not a topic we'll take lightly :-)

Guillaume

On Fri, Dec 23, 2011 at 12:38, Wujek Srujek <[hidden email]> wrote:
Hi Jochen,

of course I am aware that the idea is not well thought through at this point, just wanted to start a discussion. I lack the expertise that you have, and have no knowledge of the internals, so please be gentle ;d
As for the issues you mentioned, I am not sure, but if Groovy 3.0 allowed the new syntax (bar(a=1)) to mean the strict behavior, the new functionality would be backwards compatible? One would use the 'strict' syntax only when one is sure what parameters there are, and the current 'loose' syntax could be used as well.  Naturally, people might dislike the idea of new syntax.. The thing is, I am not that experienced in Groovy (I have been using it for about 2 months, wrote an internal DSL for works great and have become a great fan in the process), but I always wanted this strict behavior, and found the fact that I have to check the Map / initialize it in the method itself pretty annoying (as compared to the map constructor and other Groovy stuff; it is nothing even close to Java-annoying). If the idea is dismissed as nonsense, I can live with that, but it was worth a shot ;d

wujek

On Fri, Dec 23, 2011 at 12:23 PM, Jochen Theodorou <[hidden email]> wrote:
Am 22.12.2011 14:28, schrieb Wujek Srujek:
[...]

import java.lang.reflect.*
import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME)
@interface Named {
    String value()
}

class Foo {
    // the bytecode generated by the backend always annotated
parameters with their names, so this is always available
    def method(@Named('a') int a, @Named('b') int b) {
        println a + b
    }
}
>
def names = Foo.getMethod('method', int,
int).getParameterAnnotations().flatten().grep(Annotation).collect {
it.value() }
assert names == ['a', 'b']

[...]

The idea is that the compiler backend that you guys have knows the
parameter names (via the AST it works with), and annotates _each_
parameter with @Named(name) when it generates the bytecode. Then, the
runtime dispatch mechanism retrieves the list of parameter names and
uses it to correctly feed the method / function, reporting errors when
there is a typo / an unknown named parameter is used.

that will make the method selection much more difficult. That is no problem we cannot solve of course. But there is one usage that doesn't fit what you have said so far... for example:

def foo(Map m) {
 bar(m)
}

in this case foo may not know all the parameters of bar. With your requirement for reporting errors, this can no longer work. Also the Map collecting first parameter would be replaced I guess, but there is no "collect all (surplus) entries" in your suggestion. That means the call to bar works only if bar is using a map. Trying to keep the map style invocation provokes some problems. For example in

def bar(Map m){...}
bar(m:1)

Is this now supposed to call bar with the Map [m:1], or is it supposed to throw an error, because m should be a Map and no int?



This could be much safer than the map approach now - currently, the maps
accept just anything. Moreover, the developers are now forced to iterate
over the map to initialize all params that were not specified / check if
they were specified, whereas this new approach would do it
automatically.

that's true.


It would work kind of the same way the default map-based
constructor does, when it iterates over the keys and checks if there is
a field with the same name, failing when some name is unknown.

not failing right away, first we need to check the other methods of the same name if they they are there.


Could this approach somehow be incorporated into Groovy, so that we get
real named arguments, with the runtime asserting the validity for us? I
expect this to be pretty tough due to backwards compatibility, but I
guess Groovy 2.0 is open for such changes?

We decided to change the version scheme, 1.9 will be 2.0 and the next major version will then be not 2.1, but 3.0. I think for the new 2.0 it is too late, since we want to release early next year. So earliest possible point would be 3.0. But basically it is open for that, yes. But as I pointed out above we have to discuss this in terms of backwards compatibility a bit more.


Also, I am not sure about
builders and other code that relies on the flexibility to use maps, so
maybe the syntax could be extended, like:
method(a: 1, b:2)
being what it is now - loose and flexible, and
method(a=1, b=2)
using the new, strict semantics, with the checks and so on?

I would like to hear others about this one.


I am just thinking loosely, as I am a Python user and the second syntax
is supported there, and even suggested in certain use cases. Sometimes
the map approach is too 'loose', having to do the checks by myself and
so on. Maybe this could be changed or at least considered?

We normally always at least consider the changes ;)

bye blackdrag

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

  http://xircles.codehaus.org/manage_email






--
Guillaume Laforge
Groovy Project Manager
Head of Groovy Development at SpringSource
http://www.springsource.com/g2one
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Named arguments and maps

Wujek Srujek
Well, as developers of a pretty popular and used language, I would expect you to consider all pros and cons, which is a Good Thing. I am not striving for breaking backwards compatibility, that isn't a solution at all - I just wanted to convey an of mine so that it might spark some better ones.

Thanks for your answers.

wujek


On Fri, Dec 23, 2011 at 12:43 PM, Guillaume Laforge <[hidden email]> wrote:
It's definitely not an idea to be dismissed as nonsense, as we'd love to have "real" named arguments working.

But we obviously have some concerns, regarding speed of method selection, whether JDK 9 might provide some more support here (for example encoding parameter names in the bytecode), and obviously, backward compatibility issues, and so on.

So it's not a topic we'll take lightly :-)

Guillaume


On Fri, Dec 23, 2011 at 12:38, Wujek Srujek <[hidden email]> wrote:
Hi Jochen,

of course I am aware that the idea is not well thought through at this point, just wanted to start a discussion. I lack the expertise that you have, and have no knowledge of the internals, so please be gentle ;d
As for the issues you mentioned, I am not sure, but if Groovy 3.0 allowed the new syntax (bar(a=1)) to mean the strict behavior, the new functionality would be backwards compatible? One would use the 'strict' syntax only when one is sure what parameters there are, and the current 'loose' syntax could be used as well.  Naturally, people might dislike the idea of new syntax.. The thing is, I am not that experienced in Groovy (I have been using it for about 2 months, wrote an internal DSL for works great and have become a great fan in the process), but I always wanted this strict behavior, and found the fact that I have to check the Map / initialize it in the method itself pretty annoying (as compared to the map constructor and other Groovy stuff; it is nothing even close to Java-annoying). If the idea is dismissed as nonsense, I can live with that, but it was worth a shot ;d

wujek

On Fri, Dec 23, 2011 at 12:23 PM, Jochen Theodorou <[hidden email]> wrote:
Am 22.12.2011 14:28, schrieb Wujek Srujek:
[...]

import java.lang.reflect.*
import java.lang.annotation.*

@Retention(RetentionPolicy.RUNTIME)
@interface Named {
    String value()
}

class Foo {
    // the bytecode generated by the backend always annotated
parameters with their names, so this is always available
    def method(@Named('a') int a, @Named('b') int b) {
        println a + b
    }
}
>
def names = Foo.getMethod('method', int,
int).getParameterAnnotations().flatten().grep(Annotation).collect {
it.value() }
assert names == ['a', 'b']

[...]

The idea is that the compiler backend that you guys have knows the
parameter names (via the AST it works with), and annotates _each_
parameter with @Named(name) when it generates the bytecode. Then, the
runtime dispatch mechanism retrieves the list of parameter names and
uses it to correctly feed the method / function, reporting errors when
there is a typo / an unknown named parameter is used.

that will make the method selection much more difficult. That is no problem we cannot solve of course. But there is one usage that doesn't fit what you have said so far... for example:

def foo(Map m) {
 bar(m)
}

in this case foo may not know all the parameters of bar. With your requirement for reporting errors, this can no longer work. Also the Map collecting first parameter would be replaced I guess, but there is no "collect all (surplus) entries" in your suggestion. That means the call to bar works only if bar is using a map. Trying to keep the map style invocation provokes some problems. For example in

def bar(Map m){...}
bar(m:1)

Is this now supposed to call bar with the Map [m:1], or is it supposed to throw an error, because m should be a Map and no int?



This could be much safer than the map approach now - currently, the maps
accept just anything. Moreover, the developers are now forced to iterate
over the map to initialize all params that were not specified / check if
they were specified, whereas this new approach would do it
automatically.

that's true.


It would work kind of the same way the default map-based
constructor does, when it iterates over the keys and checks if there is
a field with the same name, failing when some name is unknown.

not failing right away, first we need to check the other methods of the same name if they they are there.


Could this approach somehow be incorporated into Groovy, so that we get
real named arguments, with the runtime asserting the validity for us? I
expect this to be pretty tough due to backwards compatibility, but I
guess Groovy 2.0 is open for such changes?

We decided to change the version scheme, 1.9 will be 2.0 and the next major version will then be not 2.1, but 3.0. I think for the new 2.0 it is too late, since we want to release early next year. So earliest possible point would be 3.0. But basically it is open for that, yes. But as I pointed out above we have to discuss this in terms of backwards compatibility a bit more.


Also, I am not sure about
builders and other code that relies on the flexibility to use maps, so
maybe the syntax could be extended, like:
method(a: 1, b:2)
being what it is now - loose and flexible, and
method(a=1, b=2)
using the new, strict semantics, with the checks and so on?

I would like to hear others about this one.


I am just thinking loosely, as I am a Python user and the second syntax
is supported there, and even suggested in certain use cases. Sometimes
the map approach is too 'loose', having to do the checks by myself and
so on. Maybe this could be changed or at least considered?

We normally always at least consider the changes ;)

bye blackdrag

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

  http://xircles.codehaus.org/manage_email






--
Guillaume Laforge
Groovy Project Manager
Head of Groovy Development at SpringSource
http://www.springsource.com/g2one

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Named arguments and maps

Jochen Theodorou
In reply to this post by Wujek Srujek
Am 23.12.2011 12:38, schrieb Wujek Srujek:
> Hi Jochen,
>
> of course I am aware that the idea is not well thought through at this
> point, just wanted to start a discussion. I lack the expertise that you
> have, and have no knowledge of the internals, so please be gentle ;d

I try ;)

> As for the issues you mentioned, I am not sure, but if Groovy 3.0
> allowed the new syntax (bar(a=1)) to mean the strict behavior, the new
> functionality would be backwards compatible?

yes and no... currently bar(a=1) is already allowed. It assigns the
value 1 to a and "returns" 1, which is then used for the method
invocation. := would be still free. On the other hand I don't think
bar(a=1) is a thing we have to keep.

> One would use the 'strict'
> syntax only when one is sure what parameters there are, and the current
> 'loose' syntax could be used as well.  Naturally, people might dislike
> the idea of new syntax..

well, I hope those people comment here ;)

bye blackdrag
--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Named arguments and maps

Dinko Srkoč
On 23 December 2011 13:16, Jochen Theodorou <[hidden email]> wrote:

> Am 23.12.2011 12:38, schrieb Wujek Srujek:
> [...]
>> As for the issues you mentioned, I am not sure, but if Groovy 3.0
>> allowed the new syntax (bar(a=1)) to mean the strict behavior, the new
>> functionality would be backwards compatible?
>
>
> yes and no... currently bar(a=1) is already allowed. It assigns the value 1
> to a and "returns" 1, which is then used for the method invocation. := would
> be still free. On the other hand I don't think bar(a=1) is a thing we have
> to keep.
>

I think having true named parameters is a nice feature to have. Having
said that, the current behaviour of bar(a=1) is consistent with how
Groovy treats assignments. Also, changing the semantics might break
some code existing out there. Consider this:

  def a
  bar(a = 1)
  assert a == 1 // this being consistent with ...
  assert (a = 2) == 2 // ... this

I know I have written code that depends on the fact that the assigned
value is returned as a result of the assignment expression (though not
necessarily the actual 'def a; b(a=1); use a' idiom).

Cheers,
Dinko

>
>> One would use the 'strict'
>> syntax only when one is sure what parameters there are, and the current
>> 'loose' syntax could be used as well.  Naturally, people might dislike
>> the idea of new syntax..
>
>
> well, I hope those people comment here ;)
>
>
> bye blackdrag
> --
> Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
> blog: http://blackdragsview.blogspot.com/
> german groovy discussion newsgroup: de.comp.lang.misc
> For Groovy programming sources visit http://groovy-lang.org
>
>
> ---------------------------------------------------------------------
> To unsubscribe from this list, please visit:
>
>   http://xircles.codehaus.org/manage_email
>
>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Loading...