A couple questions regarding record types:

record Person(String name, Date dob) {
  public Person {
    // ...
  }
}

1) Was it by design to have Person(String) and Person() constructors created 
for the example above?  I would prefer to see the canonical constructor and the 
map constructor only.  That is, TupleConstructor(defaults=false).  Then if I 
wanted the others, I can add default arguments to any of the components or set 
defaults to true explicitly.

2) Is the modifier required for the compact constructor by design?  If I remove 
"public" above it fails to parse.  Java allows the compact constructor without 
visibility modifier.

3) Did the copyWith() or components() ideas get dropped?  I am not seeing them 
in 4.0b2.

4) Since the compact constructor is implemented via TupleConstructor(pre=...), 
there is no error or warning if I provide a pre closure and a compact 
constructor.  Should there be an error or warning?


From: OCsite <o...@ocs.cz>
Sent: Tuesday, November 2, 2021 2:07 PM
To: MG <mg...@arscreat.com>
Cc: Groovy_Developers <dev@groovy.apache.org>; pa...@asert.com.au
Subject: [EXT] Re: Record enhancements

External Email: Use caution with links and attachments.

As for tersity, I presume the actual usage would look like „foo as Map“ or „foo 
as List“ anyway, which is actually one less keypress :), and — which in my 
personal opinion is considerably more important — it offers better consistency 
and polymorphism.

(I know next to nothing of Intellisense, but I guess it should offer the as 
operator with a selection of known types, should it not?)

Still I might be missing something of importance, of course.

All the best,
OC


On 2 Nov 2021, at 19:17, MG <mg...@arscreat.com<mailto:mg...@arscreat.com>> 
wrote:

Hmmm, yes, that would be an option.
More terse & can be discovered via Intellisense are two reasons I could think 
of that speak for the toList()/toMap() approach...

Cheers,
mg

On 02/11/2021 12:48, OCsite wrote:

Hi there,

I am probably missing something obvious here, but why adding separate methods 
for this instead of simply reusing asType?

Thanks and all the best,
OC


On 2. 11. 2021, at 8:35, Paul King 
<pa...@asert.com.au<mailto:pa...@asert.com.au>> wrote:

Thanks for the feedback! I added "toMap()" to the PR.

On Tue, Nov 2, 2021 at 7:02 AM MG 
<mg...@arscreat.com<mailto:mg...@arscreat.com>> wrote:

Hi Paul,

quick "from the top of my head" reply:

copyWith(...): Sounds like a  great idea, I have record-like classes in use, 
and the need for something like this arises immediately in practice
getAt(int): Don't see why not, might be useful
toList(): Destructuring should show its strength when pattern matching is 
introduced - outside of pattern matching right now I don't see much 
application/need for such functionality (but would be interested to see some 
practical examples :-) ), but having it does not seem to hurt.
components(): Same as getAt; the name seems quite long, maybe we can come up 
with something more terse ?
toMap(): If we have toList(), would a toMap() make sense, so that the map could 
be modified and passed as a record ctor argument to create a new record ?

Cheers,
mg


On 01/11/2021 16:14, Paul King wrote:

Hi folks,

I will be ready for a new Groovy 4 release shortly. I am interested in
folks' thoughts on records as they have gone through a few changes
recently (documented in [1], [2] and [3]) and there is a proposal[4]
for a few more enhancements.

There is a "copyWith" method (still undergoing some refactoring)
similar to the copy method in Scala and Kotlin which allows one record
to be defined in terms of another. It can be disabled if you really
must have Java-like records. The refactoring of that method hit a
slight glitch, so might not work if you grab the latest source but
should be fixed shortly.

   record Fruit(String name, double price) {}
   def apple = new Fruit('Apple', 11.6)
   assert apply.toString() == 'Fruit[name=Apple, price=11.6]'
   def orange = apple.copyWith(name: 'Orange')
   assert orange.toString() == 'Fruit[name=Orange, price=11.6]'

There is a "getAt(int)" method to return e.g. the first component with
myRecord[0] following similar Groovy conventions for other aggregates.
This is mostly targeted at dynamic Groovy as it conveys no typing
information. Similarly, there is a "toList" (current name but
suggestions welcome) method which returns a Tuple (which is also a
list) to return all of the components (again with typing information).

   record Point(int x, int y, String color) {}
   def p = new Point(100, 200, 'green')
   assert p[0] == 100
   assert p[1] == 200
   assert p[2] == 'green'
   def (x, y, c) = p.toList()
   assert x == 100
   assert y == 200
   assert c == 'green'

There is also an optional (turned on by an annotation attribute)
"components" method which returns all components as a typed tuple,
e.g. Tuple1, Tuple2, etc. This is useful for Groovy's static nature
and is automatically handled by current destructuring (see the tests
in the PR). The limitation is that we currently only go to Tuple16
with our tuple types - which is why I made it disabled by default.

   @RecordBase(componentTuple=true)
   record Point(int x, int y, String color) { }

   @TypeChecked
   def method() {
       def p1 = new Point(100, 200, 'green')
       def (int x1, int y1, String c1) = p1.components()
       assert x1 == 100
       assert y1 == 200
       assert c1 == 'green'

       def p2 = new Point(10, 20, 'blue')
       def (x2, y2, c2) = p2.components()
       assert x2 * 10 == 100
       assert y2 ** 2 == 400
       assert c2.toUpperCase() == 'BLUE'
   }

An alternative would be to follow Kotlin's approach and just have
typed methods like "component1", "component2", etc. We might want to
follow that convention or we might want to follow our TupleN naming,
e.g. "getV1", "getV2", etc. We would need to augment the Groovy
runtime and type checker to know about records if we wanted to support
destructuring but we could avoid the "toList" method and "components"
method with its size limitation if we did add such support.

Any feedback welcome,

Cheers, Paul.
P.S. Records are an incubating feature - hence may change in backwards
incompatible ways, particularly until we hit Groovy 4 final.

[1] 
https://github.com/apache/groovy/blob/master/src/spec/doc/_records.adoc<https://urldefense.com/v3/__https:/github.com/apache/groovy/blob/master/src/spec/doc/_records.adoc__;!!GFN0sa3rsbfR8OLyAw!NyOeTO41BsAx72RYKaFkHpjzc7rfbQO2ajvOqriwA1vxqYxsGsMsTEVmIQKC-qtDO--XcfI$>
[2] 
https://github.com/apache/groovy/blob/master/src/spec/test/RecordSpecificationTest.groovy<https://urldefense.com/v3/__https:/github.com/apache/groovy/blob/master/src/spec/test/RecordSpecificationTest.groovy__;!!GFN0sa3rsbfR8OLyAw!NyOeTO41BsAx72RYKaFkHpjzc7rfbQO2ajvOqriwA1vxqYxsGsMsTEVmIQKC-qtDCIm0GQs$>
[3] 
https://github.com/apache/groovy-website/blob/asf-site/site/src/site/wiki/GEP-14.adoc<https://urldefense.com/v3/__https:/github.com/apache/groovy-website/blob/asf-site/site/src/site/wiki/GEP-14.adoc__;!!GFN0sa3rsbfR8OLyAw!NyOeTO41BsAx72RYKaFkHpjzc7rfbQO2ajvOqriwA1vxqYxsGsMsTEVmIQKC-qtDPjyENMk$>
[4] 
https://issues.apache.org/jira/browse/GROOVY-10338<https://urldefense.com/v3/__https:/issues.apache.org/jira/browse/GROOVY-10338__;!!GFN0sa3rsbfR8OLyAw!NyOeTO41BsAx72RYKaFkHpjzc7rfbQO2ajvOqriwA1vxqYxsGsMsTEVmIQKC-qtDt01uPzI$>



Reply via email to