feature request: add support for Destructuring assignment

355 views
Skip to first unread message

Vito De Tullio

unread,
Sep 24, 2023, 4:15:49 PM9/24/23
to Project Lombok
Hi.
I would like to propose a new feature in lombok, that may be useful.
Assuming I have a POJO with a getFoo() method (plain or obtained via @Getters)
I want to be able to define a variable foo via something like

    var { foo } = myPojo;

with a semantics like

    var foo = myPojo.getFoo();

Do you think this kind of preprocessing could be possible?

jhma...@gmail.com

unread,
Sep 25, 2023, 8:50:22 AM9/25/23
to project...@googlegroups.com

„destructuring assignment“ – like the subject said – would make more sense in a more complex scenario:

(inspired by Perl)

 

var { name, firstname, adress } = person;

 

resulting in

 

var name = person.getName();

var firstname = person.getFirstname();

var adress = person.getAdress();

 

With this, the chosen variable name must fit with an existing getter.

 

But if Lombok should „implement some magic logic“, the original code must be valid Java code.

I am not sure about the language spec, but I think that somple is invalid.

 

Maybe more:

@Destruct(„name“, „firstname“, „adress“) person; // basically an empty instruction; maybe Lombok could replace this by the code block above

System.out.printf(„%s, %s: %s%n“, name, firstname, adress);

 

 

Jan

--
You received this message because you are subscribed to the Google Groups "Project Lombok" group.
To unsubscribe from this group and stop receiving emails from it, send an email to project-lombo...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/project-lombok/8ffedb2c-4b44-4ff8-8b99-eaf0e055516dn%40googlegroups.com.

guillaume LE LOUËT

unread,
Sep 25, 2023, 10:56:08 AM9/25/23
to Project Lombok
I think the idea is good but the issue I can think of is, is that even java syntax ? Can we have an array assignment as a var declaration ?

This code does not compile :

public static void main(String[] args) {

var person = new Object();

var { name, firstname, adress } = person;

}


Syntax error on token "}", Expression expected after this token


Even if I try

public static void main(String[] args) {

var a,b,c ="";

}


then

'var' is not allowed in a compound declaration


The best I can think of, is a lombox method to extract all the instance fields in a constant structure.

Something like

public static void main(String[] args) {

var person = new Person("me");

var o = person.conStruct();

out.prinln(o.name);

}


With conStruct() being lombook addition ( @ConStruct ) and stands for Constant Structure. This creates a sub class with only final fields and one constructor that takes the modified class as parameter.
The annotation has a variable which is the name of the generated method (conStruct is default) , so can be applied with different names
The annotation can also be applied to fields to enable/disable them specifically
The annotation can also be applied to methods to use them as accessor IFF the corresponding method is added.

example @ConStruct @ConStruct(value="publicFields" fld="last_update")  public long retrieveLastUpdate(){...}
that means that if the class has the @ConStruct then it will have a method conStruct() that returns an object with a "retrieveLastUpdate" field of type long. It also means that if the class has a @conStruct("publicFields") then it will have a publicFields() method that returns an object with a last_update field.

jhma...@gmail.com

unread,
Sep 25, 2023, 12:59:23 PM9/25/23
to project...@googlegroups.com

Playing with possible syntax this would work on Java 17:

 

public class LombokTest {

 

    public static void main(String[] args) {

        Object person = null;

        @Deconstruct({"name", "lastname", "adress"}) var unused = person;

        System.out.println("Runs fine");

    }

 

    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)

    @java.lang.annotation.Target(java.lang.annotation.ElementType.LOCAL_VARIABLE)

    public @interface Deconstruct {

        String[] value();

    }

 

}

 

Because the given value() must fit to the getter, you could also introduce a name mapping:

 

        @Deconstruct({"name", "lastname", "postadress:getAdress"}) var unused = person;

              // var name = person.getName();

        // var lastname = person.getLastname();

        // var postadress = person.getAdress();

guillaume LE LOUËT

unread,
Sep 25, 2023, 3:13:41 PM9/25/23
to Project Lombok
I still don't like this.

1. you are adding a unused var to the scope, can lead to compilation issues.
2. it becomes tedious to use if you need to use it several times, eg for comparing several persons
3. it's difficult to refactor the code, because if you change the variable "name" to "firstName" you may not notice the difference.
4. it's more personal, but I don't like having object-relative variables out of that object. I think it's cleaner to have an auto-generated object that represents what you want to do inside the object  (though I agree it's not possible if you can't modify the class)
5. it can also be difficult to find the real type of each variable, typically for auto completion.

Yet I agree that if you don't own the modified class it's not possible to do otherwise (point 4.)

Vito De Tullio

unread,
Sep 26, 2023, 1:46:18 AM9/26/23
to Project Lombok
Il giorno lunedì 25 settembre 2023 alle 14:50:22 UTC+2 Jan ha scritto:

„destructuring assignment“ – like the subject said – would make more sense in a more complex scenario:

well, absolutely :D that was the "bare minimum" example. But yeah, the idea is to be able to define one or more variable "automagically" - using the name of the variables to infer the name of the "getter" to use. I would also like to support the "final" keyword too...
 

But if Lombok should „implement some magic logic“, the original code must be valid Java code.


So the "original source" should still compile?
That is a strict requirement... toying with your @Destruct example I've tried some solutions

```java
package vito.provajava;

import lombok.Getter;

// just to check the syntax in the main method
@interface Destruct {
    public String[] value() default {};
}

@Getter
class Pojo {
    int foo;
    long bar;
    short baz; // not used
}

public class C {
    Pojo pojo;

    {
        // PRO can handle multiple fields at once
        // VERY CON `_dummy` is just a placeholder variable, the real ones are
        // `foo` and `bar`
        // CON somewhat too much verbose and "clumsy" to use
        // DOUBT can lombok create/delete local variables?
        @Destruct({ "foo", "bar" }) var _dummy = pojo;
        // foo is an int, bar is a long, _dummy doesn't exist
        // System.out.println(String.format("%s, %s", foo, bar));
    }

    {
        // CON each field on a different statement
        // CON somewhat too much verbose
        @Destruct var foo = pojo;
        @Destruct var bar = pojo;
        System.out.println(String.format("%s, %s", foo, bar));
    }

    {
        // PRO multiple assignment
        // CON still need to "copy" foo each time
        // CON cannot use var/val as it's not allowed in compound declarations
        // DOUBT can lombok change the type of local variables?
        @Destruct Object foo = pojo, bar = pojo;
        System.out.println(String.format("%s, %s", foo, bar));
    }
}
```

Vito De Tullio

unread,
Sep 26, 2023, 1:46:34 AM9/26/23
to Project Lombok
Il giorno lunedì 25 settembre 2023 alle 16:56:08 UTC+2 guillaume LE LOUËT ha scritto:
I think the idea is good but the issue I can think of is, is that even java syntax ? Can we have an array assignment as a var declaration ?

Yeah, I didn't tough about the syntax part
 
The best I can think of, is a lombox method to extract all the instance fields in a constant structure.

While this can be useful in some scenarios, I don't think it's really relevant for my use case, where
- I already start with a pojo full of fields,
- I need to use some of them multiple times,
- and I want to avoid multiple pojo.getFoo()

guillaume LE LOUËT

unread,
Sep 26, 2023, 9:49:42 AM9/26/23
to Project Lombok
Your Pojo fields are private. What's more they can be  retrieved at execution time, so varying.

Avoiding pojo.getFoo() makes no sense in itself. you can have some pojo.setFoo() inbetween which can lead to later bugs. I agree that sometimes it's very annoying , typically for DTOs, but you can later change those DTOs and having a very different behaviour.
That's why I proposed a constant-copy of the object, which can't be altered later and still be easily accessible.

Do you have an example where this would be actually useful and not bug-prone ? Maybe I misunderstood your case.

Vito De Tullio

unread,
Sep 27, 2023, 8:15:36 AM9/27/23
to project...@googlegroups.com
Il giorno mar 26 set 2023 alle ore 15:49 guillaume LE LOUËT <guillaum...@gmail.com> ha scritto:
Your Pojo fields are private. What's more they can be  retrieved at execution time, so varying.

wait.
I don't want to access the private fields.
I want to call the getters.

whatever syntax it will be, it should be translated to

    var foo = pojo.getFoo();

sorry for the confusion.
 
You received this message because you are subscribed to a topic in the Google Groups "Project Lombok" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/project-lombok/AWx5uDsX6mQ/unsubscribe.
To unsubscribe from this group and all its topics, send an email to project-lombo...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/project-lombok/3db23e2f-80b4-4ee3-a68d-592168eabf73n%40googlegroups.com.


--
Vito De Tullio - Vito.D...@gmail.com

guillaume LE LOUËT

unread,
Sep 27, 2023, 11:08:29 AM9/27/23
to Project Lombok
I know what you want to do. There is no confusion. Were you to access your pojo fields through the accessors or not does not change that they can be changed between calls.

Again,
Do you have an example where this would be actually useful and not bug-prone ?

So far you want to have

```java
{
  // MyMap::new(java.util.Map backing, String owner)
  MyMap<?,?> m = new MyMap(new HashMap<>(Map.of("a", "b")), "me");
  var hashCode = m.hashCode();
  var size=m.size();
  var owner=m.getOwner();
}
```

replaced by

```java
{
  // MyMap::new(java.util.Map backing, String owner)
  MyMap<?,?> m = new MyMap(new HashMap<>(Map.of("a", "b")), "me");
  @Deconstruct({"hashCode:hashCode", "size:size", "owner"}) var unused=m;
}
```

And I'm saying this is bug-prone because if I replace "owner" by "creator" I won't know why it fails later. OR if I used it twice for comparison, the second call :

@Deconstruct({"hashCode:hashCode", "size:size", "owner"}) var unused=m2; `

will fail because "unused" already exists, or the variables already exist, which will become tedious to handle. Meanwhile, using a constant structure method in the class allows to avoid both those issues.

Vito De Tullio

unread,
Sep 28, 2023, 5:20:38 AM9/28/23
to project...@googlegroups.com
Il giorno mer 27 set 2023 alle ore 17:08 guillaume LE LOUËT <guillaum...@gmail.com> ha scritto:
I know what you want to do. There is no confusion. Were you to access your pojo fields through the accessors or not does not change that they can be changed between calls.

Again,
Do you have an example where this would be actually useful and not bug-prone ?

So far you want to have

```java
{
  // MyMap::new(java.util.Map backing, String owner)
  MyMap<?,?> m = new MyMap(new HashMap<>(Map.of("a", "b")), "me");
  var hashCode = m.hashCode();
  var size=m.size();
  var owner=m.getOwner();
}
```

replaced by

```java
{
  // MyMap::new(java.util.Map backing, String owner)
  MyMap<?,?> m = new MyMap(new HashMap<>(Map.of("a", "b")), "me");
  @Deconstruct({"hashCode:hashCode", "size:size", "owner"}) var unused=m;
}
```


well, first of all... that is NOT what I want.
I never suggested about "extended" version with "renaming" or explicit method name (so no "hashCode:hasCode"), and my goal is just to support getters.
Also - among the possible suggestion - I like the "unused" variable the less.
I would prefer to have much lighter syntax, so I would settle to just 1 field per assignment, without parameters to `@Deconstruct`

So what I want is something like

```java
{
    // Pojo has a "getFoo()" method
    var pojo = new Pojo("bar");
    var foo = pojo.getFoo();
}
```

replaced by

```java
{
    // Pojo has a "getFoo()" method
    var pojo = new Pojo("bar");
    @Deconstruct var foo = pojo;
}
```

 
And I'm saying this is bug-prone because if I replace "owner" by "creator" I won't know why it fails later.

I expect a java compiler error... I mean, in your scenario there was a variable called `owner` and you changed id to `creator`... seems normal to me...
At the end is like when - using `@Getter` on an object, and using `getFoo` somewhere in the code - I change the field in `bar`. Compile errors will occur (and at least eclipse tells me instantly)
 

OR if I used it twice for comparison, the second call :

@Deconstruct({"hashCode:hashCode", "size:size", "owner"}) var unused=m2; `

will fail because "unused" already exists, or the variables already exist, which will become tedious to handle.

I agree with you about the `unused` double usage, but - assuming this usecase is not my proposal, my expectaction is that, as there is AST rewriting the "unused" variable should disappear.

Meanwhile, using a constant structure method in the class allows to avoid both those issues.

The constant structure method you suggest does not help my usecase.

I have a pojo (sometimes a JPA entity, sometimes a DTO, generally speaking an object full of getters and setters) and I need to extract 3/4 fields to implement my business logic.
In more than one case, I need to access the field values twice or more, so I usually keep them in local variables for the lifetime of the method.

My goal is - simply put - to avoid writing a bunch of

```java
var foo = pojo.getFoo();
var bar = pojo.getBar();
var baz = pojo.getBaz();
// actual logic that relies on foo, bar, baz
```

where I need to write `foo` and `getFoo` twice.
And in this scenario your suggestion to create another object with a `.foo` field doesn't help me... I should write something like

```java
var o = pojo.conStruct()
var foo = o.foo;
var bar = o.bar;
var baz = o.baz
// actual logic that relies on foo, bar, baz
```

did I got your proposal wrong?

jhma...@gmail.com

unread,
Sep 28, 2023, 7:53:10 AM9/28/23
to project...@googlegroups.com

Thanks for clarifying your needs.

But just for getting rid of one getter-call I think this is not worth the effort.

Additionally you have to use the special name for your local variable so it fits to the getter.

This means, that refactoring (rename the getter) or the local variable is not possible.

Having multiple calls is also not possible:

              Person p1;

              Person p2;

              var name = p1;

              var name = p2;

              // need names of p1 and p2 ???

 

I have a pojo (sometimes a JPA entity, sometimes a DTO, generally speaking an object full of getters and setters) and I need to extract 3/4 fields to implement my business logic.

In more than one case, I need to access the field values twice or more, so I usually keep them in local variables for the lifetime of the method.“

 

I dont see any benefit in holding the value in a local variable over using the getter.

You would have some if the „getter“ is expensive by calculating/loading stuff (in that case it is not a ‚real‘ getter) or if you need the „first value“ and the field is changed in the meanwhile.

 

If you want to transfer a bunch of values from one pojo to another I recommend a mapping framework like MapStruct.

guillaume LE LOUËT

unread,
Sep 28, 2023, 8:48:09 AM9/28/23
to Project Lombok
> well, first of all... that is NOT what I want.

And that I would know if you had answered me about an actual example where this would be useful and not bug-prone.


> and my goal is just to support getters.

Getters in lombok have options (eg fluent) that make your original idea not usable in many cases.


> Compile errors will occur (and at least eclipse tells me instantly)

It won't. There is a thing called "refactoring" on most IDEs. You  don't change the names by hand, you refactor them.
What's more with lombok, your accessor is built on your field so plain renaming will still work. In the example I provided, the code would STILL compile but could not find the modified field all of a sudden.


> And in this scenario your suggestion to create another object with a `.foo` field doesn't help me... I should write something like

No. You write something like  :
// actual logic that relies on o.foo, o.bar, o.baz


> I dont see any benefit in holding the value in a local variable over using the getter.

You have some if the access is lazy on a DB and the new construction is done in // .
For example if the Book have Person as authors, and Student have Person as teachers, then listing the students and the books of a Person should be done in // .

Something like
```
@Get("byId/{id}/stats")
public PersonDTOOut getStatsById(@PathParam long id){
  Person p = personService.findById(id);
  // omit checks
  PersonDTOOut dto = new PersonDTOOut();
  Person.ConStruct pc = p.conStruct();
  dto.numberOfBooks = pc.books.size();
  dto.numberOfStudents=pc.students.size();
  dto.numberOfChildren=pc.children.size();
  dto.numberOfHouses=pc.houses.size();
  return dto;
}
```

Of course this can be an issue if you have a limited number of DB connexion/concurrent requests.

Marco Servetto

unread,
Sep 29, 2023, 4:00:15 AM9/29/23
to project...@googlegroups.com
Hi,
I'm not sure if Lombock can make this work (ie, if we can find a
syntax that is valid Java)
I have a similar feature in my programming languages, where you can
write the following:

(foo,bar,x) = <expression>
and this would initialize variable foo,bar and x with the result of
the getters foo(),bar() and x() from the result of the expression.
You can append numbers at the end of the variable names to personalize
the names, for example
(x1,y1) = computePoint1()
(x2,y2) = computePoint2()
This would call getters x() and y(), not x1() or y2().

Overall, there is an ongoing discussion in the PL design community
about name base decomposition and positional decomposition (as Java
now offers with the new 'case').

Marco!

On Thu, 28 Sept 2023 at 14:48, guillaume LE LOUËT
> To view this discussion on the web visit https://groups.google.com/d/msgid/project-lombok/4b484769-518c-4f93-9c6b-7c3423ebcde5n%40googlegroups.com.

Vito De Tullio

unread,
Sep 29, 2023, 6:30:59 AM9/29/23
to project...@googlegroups.com
Il giorno gio 28 set 2023 alle ore 13:53 <jhma...@gmail.com> ha scritto:

Thanks for clarifying your needs.

But just for getting rid of one getter-call I think this is not worth the effort.


I agree with you that there is no much gain, but my real concern is to have as little syntax (and as little magic) as possible.
Ideally it started with a js-like
```
var { foo, bar, baz } = pojo;
```
that is *very* concise and allow the definition of multiple variables in one place.
While I don't dislike the "abuse" of the `@Deconstruct` syntax, adding all the varialble definition there AND defining a "dummy" variable was too much IMHO, with the "new code" less manageable than the original one.

This is the reason I may prefer to settle to a more simple

```
@Decorated var foo = pojo;
@Decorated var bar = pojo;
@Decorated var baz = pojo;
```

but I can understand why you think it's not worth the effort
 

Additionally you have to use the special name for your local variable so it fits to the getter.

This means, that refactoring (rename the getter) or the local variable is not possible.


well, that a feature for me :^)
I agree that it's a "special case", but honestly in my experience it's 80%+ of the times that the name of the variable matches the name of the getter...
and for the rest of cases the "normal" syntax is still available
 

Having multiple calls is also not possible:

              Person p1;

              Person p2;

              var name = p1;

              var name = p2;

              // need names of p1 and p2 ???


Same as above, nothing prevent to simply not using this annotation in this case.

 

Vito De Tullio

unread,
Sep 29, 2023, 6:54:58 AM9/29/23
to project...@googlegroups.com
Il giorno gio 28 set 2023 alle ore 14:48 guillaume LE LOUËT <guillaum...@gmail.com> ha scritto:

> well, first of all... that is NOT what I want.
And that I would know if you had answered me about an actual example where this would be useful and not bug-prone.

Sorry, but I really don't understand your animosity here.
Have a look at the very first message of this thread:

>>>  Assuming I have a POJO with a getFoo() method (plain or obtained via @Getters)
>>>  I want to be able to define a variable foo via something like
>>> 
>>>      var { foo } = myPojo;
>>> 
>>>  with a semantics like
>>> 
>>>      var foo = myPojo.getFoo();
 
what doubt do you have about this simple description? Do you get that my goal was just to avoid the repetition of "foo" and "getFoo"?


> and my goal is just to support getters.
Getters in lombok have options (eg fluent) that make your original idea not usable in many cases.

yes, my idea is not meant to be used in all possible combinations. It's meant to be used in very specific (but honestly very common) scenarios, where getters the "classical" ones.
 
> Compile errors will occur (and at least eclipse tells me instantly)
It won't. There is a thing called "refactoring" on most IDEs. You  don't change the names by hand, you refactor them.

I was replying specifically to your comment:

>>>  ```java
>>>  {
>>>    // MyMap::new(java.util.Map backing, String owner)
>>>    MyMap<?,?> m = new MyMap(new HashMap<>(Map.of("a", "b")), "me");
>>>    @Deconstruct({"hashCode:hashCode", "size:size", "owner"}) var unused=m;
>>>  }
>>>  ```
>>> And I'm saying this is bug-prone because if I replace "owner" by "creator" I won't know why it fails later.

The only way to replace "owner" by "creator" is doing it by hand, as those are strings in your example, and I expect a bunch of errors if you do it. Do you agree with me or not?

 
> And in this scenario your suggestion to create another object with a `.foo` field doesn't help me... I should write something like

No. You write something like  :
// actual logic that relies on o.foo, o.bar, o.baz


> I dont see any benefit in holding the value in a local variable over using the getter.

You have some if the access is lazy on a DB and the new construction is done in // .
For example if the Book have Person as authors, and Student have Person as teachers, then listing the students and the books of a Person should be done in // .

Something like
```
@Get("byId/{id}/stats")
public PersonDTOOut getStatsById(@PathParam long id){
  Person p = personService.findById(id);
  // omit checks
  PersonDTOOut dto = new PersonDTOOut();
  Person.ConStruct pc = p.conStruct();
  dto.numberOfBooks = pc.books.size();
  dto.numberOfStudents=pc.students.size();
  dto.numberOfChildren=pc.children.size();
  dto.numberOfHouses=pc.houses.size();
  return dto;
}
```

...I still don't see any value in this: thinking about what the original source, it could have been written just as

  PersonDTOOut dto = new PersonDTOOut();
  dto.numberOfBooks = p.getBooks().size();
  dto.numberOfStudents=p.getStudents().size();
  dto.numberOfChildren=p.getChildren().size();
  dto.numberOfHouses=p.getHouses().size();
  return dto;

what's the gain of your construct?



guillaume LE LOUËT

unread,
Sep 30, 2023, 9:40:31 AM9/30/23
to Project Lombok

> what doubt do you have about this simple description? Do you get that my goal was just to avoid the repetition of "foo" and "getFoo"?

Well … it's useless. I asked for a *useful* and error-prone use case.
as you write later there is not gain :

> thinking about what the original source, it could have been written just as

> var foo = myPojo.getFoo();

I on the other hand can see a specific use case where the difference makes sense.



> ..I still don't see any value in this: thinking about what the original source, it could have been written just as

Not under the premises of my example. The rewriting you made is sequential.

There is a gain in that example if as I wrote explicitly in my previous message

> and the new construction is done in // .

example :

```java

class Person {

@Getter(lazy=true)

private final List<Book> books=fetchBooksFromDB();


private List<Book> fetchBooksFromDB() {

// access the DB, typically with service or direct DB query

return null;

}

// other fields that require a DB access

// the rest is built by lombok :

@AllArgsConstructor

public static class ConStruct{

public final List<Book> books;

}


public ConStruct conStruct(){

// future for a field, associated to its accessor

Future<List<Book>> f_books = ForkJoinPool.commonPool().submit(()->getBooks() );

// other accessors and futures

// ...

try {

return new ConStruct(

f_books.get()

// access the other futures

);

} catch (InterruptedException | ExecutionException e) {

throw new RuntimeException(e);

}

}


}

```

> Sorry, but I really don't understand your animosity here.

Please read what is written and answer what is asked. That would help a lot make the discussion better.

Vito De Tullio

unread,
Sep 30, 2023, 10:36:30 AM9/30/23
to project...@googlegroups.com
Il giorno sab 30 set 2023 alle ore 15:40 guillaume LE LOUËT <guillaum...@gmail.com> ha scritto:
> what doubt do you have about this simple description? Do you get that my goal was just to avoid the repetition of "foo" and "getFoo"?

Well … it's useless. I asked for a *useful* and error-prone use case.

I think that is the real issue here. You expect that this change must enable you other features, hidden by current implementation.
What I'm trying to convey here is that the mere syntax sugar I am talking about it the whole goal.
I just want a (light) syntax that allows me to express more concisely a very simple statement.

You may find it not useful, but that's just it. I, instead, find it useful enough (and general enough) that could be worth it.
I would have preferred a (light) syntax that allowed me to set multiple variable at the same time, but that in itself is still worth it, in my opinion.

I have encountered a very number of times where I just wanted (for whatever reasons... performance, logging, or just for clarity of the source code) to keep a separate variable to hold the value of a getter.
And I know that sometimes I have messed up, saving in the variable `foo` the value of `getBar()` (this is also easier when `var` is used).

And I think that this issue is very common among even other developers.
At least in the JavaScript world (where whole careers have been devoted to building and maintaining source-to-source compilers) they extended the actual language to explicitly express this very idiom.

I will not comment on your conStruct proposal.

Marc Günther

unread,
Sep 30, 2023, 1:56:19 PM9/30/23
to Project Lombok
Sorry, but I have to chime in here, as it seems this discussion is wandering way off topic. It would be nice to keep it on the original feature that Vito proposed, which is Destructuring Assignment.

If anyone doesn't know what this is, a quick web search should be sufficient. For example here is a very good explanation on how it is implemented in JavaScript:
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

Guillaume, I have not the slightest idea how what you wrote relates to this feature here, so maybe that discussion on whatever you are proposing should be continued in a separate thread.

So what are we talking about? In Java, to extract several fields out of a given object, you currently have to write something like this:

  var person = databaseConnection.fetchPersonByID(id);
  var firstName = person.firstName;
  var lastName = person.lastName;
  var gender = person.gender;
  var birthDate = person.birthDate;

Vitos proposal is to have a syntax like this:

  var {firstName, lastName, gender, birthDate} = databaseConnection.fetchPersonByID(id);

I think it is quite obvious that this is far more readable then the original, so we don't really need a discussion if this is a useful feature or not.

So to get back to the topic at hand. The interesting question is, is this something that Lombok can do in a reasonable way, and how so?

There are two constraints:
- It must use an annotation, otherwise Lombok won't even run.
- It must be valid Java syntax, otherwise Lombok won't even run.

First point is easy to address, we could define a class or package scoped annotation like @EnableDestructuringAssignment which enables the feature for the entire class/package.

The second part is trickier. There have been some proposals, which have been lost in this rather strange discussion above, so let's repeat them here:

a)
  @Deconstruct({"firstName", "lastName", "gender", "birthDate"}) var unused = databaseConnection.fetchPersonByID(id);

b)
  var person = databaseConnection.fetchPersonByID(id);
  @Destruct var firstName = person;
  @Destruct var lastName = person;
  @Destruct var gender = person;
  @Destruct var birthDate = person;

c)
  var person = databaseConnection.fetchPersonByID(id);
  @Destruct Object firstName = person, lastName = person, gender = person, birthDate = person;

I don't really like any of these. None of it has the readability and beauty of the original proposal:

  var {firstName, lastName, gender, birthDate} = databaseConnection.fetchPersonByID(id);

That unused variable in a), and the actual variables declared as strings makes the first example a no no imho (although it is the only one with the possibility to do name mapping like in JavaScript). The second example b) is not really much better than the original Java code, it only removes the duplication of the variable/field name. And c) is, ah, well, I don't know, but I think it might take a while till I wouldn't scratch my head every time I see something like it. It somehow is non obvious as to what it means. Also too much repetition, it is still necessary to extract person into its own variable first.

Any other ideas?

Marc

PS: To be honest, it is not quite clear to me what constitutes "valid Java syntax". As obviously, even if it were valid syntax, it still would not compile, otherwise Lombok would not be needed. So it must be valid syntax in every way, except that it can use identifiers that don't resolve to anything useful without Lombok magic?

Marco Servetto

unread,
Sep 30, 2023, 3:23:48 PM9/30/23
to project...@googlegroups.com
lambdas are valid Java syntax, right?
so to write
var {firstName, lastName, gender, birthDate} =
databaseConnection.fetchPersonByID(id);
we can just write
(firstName,lastName,gender,birthDate)->databaseConnection.fetchPersonByID(id);
Would this work? or are lambdas are valid expressions but not valid statements?
> To view this discussion on the web visit https://groups.google.com/d/msgid/project-lombok/d90e1767-88eb-4a82-bd53-5fee6e7268d8n%40googlegroups.com.

guillaume LE LOUËT

unread,
Sep 30, 2023, 7:15:05 PM9/30/23
to Project Lombok
> Guillaume, I have not the slightest idea how what you wrote relates to this feature here, so maybe that discussion on whatever you are proposing should be continued in a separate thread.

What I wrote before was an example of when the example discussed is actually useful. Because as you wrote, and as Vito wrote, writing @Destruct var firstName = person is completely useless(should write var fistName = person.getName() ) , and all the other examples that were provided were non valid java syntax.

I was specifically answering the question from Jan :

> I dont see any benefit in holding the value in a local variable over using the getter.



> so maybe that discussion on whatever you are proposing should be continued in a separate thread.

My answer was considering the issue of name clash. Since the original proposal creates new variables with the names of the fields, if you use it while names are already assigned to variables, then this would not work.
This WILL happen if you use it twice, for example for comparison of two items, or if the variables are already present eg as parameters, therefore this initial idea is fundamentally bugged.
Someone else addressed it using name mapping in the annotation params but this will provoke bugs when refactoring.
My proposal was to place the annotation on the POJO , making a constant class that can be access using "." notation. It fixes clash and refactoring issue (also valid syntax issue) but it requires to modify the POJO which may not be possible.

This IS on topic as a way to acquire all the fields in a easily accessible manner without injecting compiling/usage bugs, with annotations.
This IS NOT on topic considering POJOs you can't modify - just like constructors, accessors in lombok.



> it is not quite clear to me what constitutes "valid Java syntax".

valid syntax means that the java compiler must not find a syntax error before lombok modifies the instructions tree.

Marc Günther

unread,
Oct 1, 2023, 4:10:36 PM10/1/23
to project...@googlegroups.com

On 30. Sep 2023, at 21:23, Marco Servetto <marco.s...@gmail.com> wrote:

lambdas are valid Java syntax, right?
so to write
var {firstName, lastName, gender, birthDate} =
databaseConnection.fetchPersonByID(id);
we can just write
(firstName,lastName,gender,birthDate)->databaseConnection.fetchPersonByID(id);
Would this work? or are lambdas are valid expressions but not valid statements?

So that would be

d)

(firstName,lastName,gender,birthDate) -> databaseConnection.fetchPersonByID(id);

This is certainly valid syntax, but so far it's just a Lambda expression, we need a way to tell the compiler and our fellow developers that this should do a destructuring operation. I don't think we can put an annotation on an arbitrary expression?

Ideas anyone?

On 1. Oct 2023, at 01:15, guillaume LE LOUËT <guillaum...@gmail.com> wrote:

> Guillaume, I have not the slightest idea how what you wrote relates to this feature here, so maybe that discussion on whatever you are proposing should be continued in a separate thread.

What I wrote before was an example of when the example discussed is actually useful. Because as you wrote, and as Vito wrote, writing @Destruct var firstName = person is completely useless(should write var fistName = person.getName() ) , ...

Yes, the initial example was not very well chosen. This was immediately pointed out by Jan, and also confirmed by Vito the next day. No need to carry on after this.

... and all the other examples that were provided were non valid java syntax.

So what? Figuring out a valid syntax is part of the discussion here. This does not make the feature useless.

As we have by now established that this is a useful feature to have (I use it all the time in JS), can we please move on?

[...]

> so maybe that discussion on whatever you are proposing should be continued in a separate thread.

My answer was considering the issue of name clash. Since the original proposal creates new variables with the names of the fields, if you use it while names are already assigned to variables, then this would not work.
This WILL happen if you use it twice, for example for comparison of two items, or if the variables are already present eg as parameters, therefore this initial idea is fundamentally bugged.

No it isn't. It simply means that you cannot use the feature in this case. Which is no big deal as in most cases there won't be a name clash.

There is a similar issue with the import statement in Java. You cannot have:

  import java.lang.List;
  import java.awt.List;

as that will clash. So if you are in that situation, you cannot use import, and have to resort to writing the full package name every single time.

Other languages provide a feature like

  import ... as ...;

to solve that specific use case. Java doesn't. That doesn't mean that the import statement in itself is bugged, it just means that you might have to write something more inconvenient in some cases.

Regarding Destructuring Assignment, JavaScript provides name mapping to solve the name clash problem. The original feature request here doesn't. That doesn't make it useless or buggy, it is still a very useful feature to have in all the non name clash cases.

Someone else addressed it using name mapping in the annotation params but this will provoke bugs when refactoring.

No it doesn't. The Rename Refactoring in the IDE will either be extended to be smart enough to rename the annotation params as well, and if not, the compiler will flag them as errors cause they point to now non existing variables. Either way, no problem.

My proposal was to place the annotation on the POJO , making a constant class that can be access using "." notation. It fixes clash and refactoring issue (also valid syntax issue) but it requires to modify the POJO which may not be possible.

This IS on topic as a way to acquire all the fields in a easily accessible manner without injecting compiling/usage bugs, with annotations.
This IS NOT on topic considering POJOs you can't modify - just like constructors, accessors in lombok.

So that would be:

e)
  var p = databaseConnection.fetchPersonByID(id).destructure();
  // use p.firstName, p.lastName, etc instead of firstName, lastName, etc in the following code

There would actually no need to annotate the pojo here. Simply put @EnableDestructuringAssignment on this class, which adds the destructure() method to all used classes, similar to what @ExtensionMethod does.

I still don't really see the value of this. The goal was to have local variables. With this I just have a copy of the original object, with fields instead of getters.

Plain Java already allows to write this:
  var p = databaseConnection.fetchPersonByID(id);
  // use p.getFirstName(), p.getLastName(), etc instead of firstName, lastName, etc in the following code

which is not very different from e), except when the getters are called multiple times and are expensive to call.

> it is not quite clear to me what constitutes "valid Java syntax".

valid syntax means that the java compiler must not find a syntax error before lombok modifies the instructions tree.


When I look through all the syntax suggestions so far, I don't think Lombok is a good fit for this feature. It doesn't feel lomboky. All the rest of Lombok doesn't really change the syntax in such drastic ways, even @ExtensionMethod is tame compared to the examples here, and even that is already too much magic for some people. I most certainly do not see that maintainers would agree to something like this.

I still agree that it would be a nice feature to have, but Java is missing so many things that might also be useful, like Optional Chaining (?.), null coalescing operator (??), nice syntax for array and map literals, and and and... Java is becoming old and rusty, and unfortunately, Java language designers to not do much to keep it modern. But that is a different discussion.

guillaume LE LOUËT

unread,
Oct 1, 2023, 8:48:17 PM10/1/23
to Project Lombok
> Yes, the initial example was not very well chosen. This was immediately pointed out by Jan, and also confirmed by Vito the next day. No need to carry on after this.

I am refuting your claim that my intervention are not on topic, so yes I had to explain this again since you did not get it by yourself.
So I'm not the one carrying on it. YOU are.
If you don't want to be answered, don't talk.


> So what? Figuring out a valid syntax is part of the discussion here. This does not make the feature useless.

So I did not consider them.
And why are you quoting this and talking about useless ? That's not something I wrote, stop pretending I did.



> No it isn't. It simply means that you cannot use the feature in this case. Which is no big deal as in most cases there won't be a name clash.

There won't be a name clash because people will avoid it. Does not mean it's not bugged. People avoiding a bug is not a fix.


> There is a similar issue with the import statement in Java. You cannot have:

It's not an issue. it's on purpose, so that you don't erase an import with another one. Types are imported as map.

Comparing two items field by field however is a common usage of fields.


> Regarding Destructuring Assignment, JavaScript

is not the topic. Stop complaining about java and go back to javascript.

Just because a feature is in javascript, does not make it useful.



> The Rename Refactoring in the IDE will either be extended to be smart enough to rename the annotation params as well

Who is going to do that ? You ?


> if not, the compiler will flag them as errors cause they point to now non existing variables. Either way, no problem.

Yes, more problems because the error is not in the var declaration/usage but in the parameter of the annotation. Also the getters won't exist, not the vars.


> The goal was to have local variables.

No. The goal was to not repeat multiple getter access :

> - I already start with a pojo full of fields,
> - I need to use some of them multiple times,
> - and I want to avoid multiple pojo.getFoo()

local variables is a way to reduce the tediousness, not a goal in itself.


> which is not very different from e),

The same can be said of the one with variables :

using variable 
 books
is not very different from using original
 person.getBooks()

But the example I gave adds two letters only so it is
 p.book
and you don't have name clash, and you don't have refactoring issue with annotation parameters, but you say it's useless.

Precisely my point why this feature is not useful.


> I don't think Lombok is a good fit for this feature

But it is ? @Builder is a more complex example of what I propose. What I proposed IS lombok-ish way of doing it : you annotate your pojo so that additional code is injected to perform the logic. That's boilerplate reduction.

> But that is a different discussion.

So don't even bring it in the first place.

Victor Williams Stafusa da Silva

unread,
Oct 3, 2023, 12:48:16 AM10/3/23
to project...@googlegroups.com
Maybe this could be easier to actually implement?

@Destructure

var firstName, lastName, gender, birthDate = databaseConnection.fetchPersonByID(id);
--
You received this message because you are subscribed to the Google Groups "Project Lombok" group.
To unsubscribe from this group and stop receiving emails from it, send an email to project-lombo...@googlegroups.com.

Vito De Tullio

unread,
Oct 3, 2023, 4:23:59 AM10/3/23
to project...@googlegroups.com
Il giorno mar 3 ott 2023 alle ore 06:48 Victor Williams Stafusa da Silva <victor...@gmail.com> ha scritto:
Maybe this could be easier to actually implement?

@Destructure
var firstName, lastName, gender, birthDate = databaseConnection.fetchPersonByID(id);

Unfortunately my understanding is that this is not valid java syntax, so it could not be used

Marco Servetto

unread,
Oct 3, 2023, 4:42:26 AM10/3/23
to project...@googlegroups.com
>> Maybe this could be easier to actually implement?
>> @Destructure
>> var firstName, lastName, gender, birthDate = databaseConnection.fetchPersonByID(id);
Yes, we are so near:
>>Cannot use 'var' on variable without initializer,
but...
>> Destructure firstName, lastName, gender, birthDate = databaseConnection.fetchPersonByID(id);
I think this IS valid Java! It is not well typed, but it is syntactically valid.
Thus, in the same way lombok added Var as a type before we had the
'var' keyword, lombok could add a 'Destructure' or any other type name
to use as a matcher for this task!
I would love it!
Marco.

jhma...@gmail.com

unread,
Oct 3, 2023, 8:37:02 AM10/3/23
to project...@googlegroups.com
Playing with the syntax ...
But while the first variable is annotated, I don't know if the following are and if Lombok would be able to handle that.

Jan


public class Lombok {
public static void main(String[] args) {
lombok();
vanilla();
}

public static void lombok() {
@Deconstruct Object name, lastname, birthday = findPersonById(42);
System.out.println("Lombok runs fine");
}

public static void vanilla() {
// Lombok should change the type of the variables to the concrete type of the getter.
// Lombok should set their value to the result of the getter.
// Lombok should store the object in an intermediate variable:
Person findPersonById = findPersonById(42);
String name = findPersonById.getName();
String lastname = findPersonById.getLastname();
java.time.Instant birthday = findPersonById.getBirthday();
System.out.println("Vanilla runs fine");
}

public static Person findPersonById(int id) {
return new Person();
}
}

public class Person {
private String name = "Douglas";
private String lastname = "Adams";
private String adress = "not-used";
private java.time.Instant birthday = java.time.Instant.now();
public String getName() { return name; }
public String getLastname() { return lastname; }
public String getAdress() { return adress; }
public java.time.Instant getBirthday() { return birthday; }
}

public class XDeconstruct {
// Just a placeholder for creating valid Java syntax.
// Replaced by Lombok with the 'real' type.
}

@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(java.lang.annotation.ElementType.LOCAL_VARIABLE)
public @interface Deconstruct {
}




-----Ursprüngliche Nachricht-----
Von: project...@googlegroups.com <project...@googlegroups.com> Im Auftrag von Marco Servetto
Gesendet: Freitag, 29. September 2023 10:00
To view this discussion on the web visit https://groups.google.com/d/msgid/project-lombok/CAOu%2Baf%3DW0d4rBVJQ4XVkiSP6XVT%3DLkgdXhM7tfBQVxVDkhNGPg%40mail.gmail.com.

Marco Servetto

unread,
Oct 3, 2023, 8:49:31 AM10/3/23
to project...@googlegroups.com
In my suggestion (using a Lombock type instead of a lombok annotation)
all of the variables declared in the list of comma separated variables
would be of the lombok type, so... it should be possible to make it
work
> To view this discussion on the web visit https://groups.google.com/d/msgid/project-lombok/00cb01d9f5f6%244b94e770%24e2beb650%24%40gmail.com.

guillaume LE LOUËT

unread,
Oct 3, 2023, 2:36:49 PM10/3/23
to Project Lombok
Iombok works by processing annotations. It gets the syntax tree once parsed and modifies it.
So no annotation=no lombok work.

L0mbok can create vars, so you don't need to explicitly declare them. So `@Deconstruct Object unused=myperson` is totally fine and can create the variables for each field in the Person.
However this will create name clash, for example if you add a field "size" in Person while also using a var "size" in your call : your explicit variable will fail and you won't be able to know why. Or again, you can't compare two persons field by field.
A partial workaround could be to allow to specify the vars used, eg `@Deconstruct Object name, size, age=myperson`. Also yes, the annotation is applied to each field. This would allow to not include non-specified fields.

Since declaring a variable twice is correct syntax, we could even process the same variable declaration as extracting all the fields, while declaring new variables would instead extract only those variables.
So `@Deconstruct var myperson=myperson` would be replaced by `var name=myperson.getName(), age=myperson.getAge(), size=myperson.getSize(), isAlive=myPerson.isAlive();`
While `@Deconstruct Object name, age=myperson` would be replaced by `var name=myperson.getName(), age=myperson.getAge();`.
However the issue of name clashes still remains for comparison. And what if a field has same name as the object we are using ? eg `MyList list = new MyList(); list.setList(new ArrayList()); @Deconstruct var list=list;`

Then of course we need to agree on how to select the fields : all fields including parent ones ? All fields that have ONE getter associated ?

Marco Servetto

unread,
Oct 3, 2023, 2:45:29 PM10/3/23
to project...@googlegroups.com
The idea was to use multiple local variables separated by comma to
select what getters to call.
Since it is uncommon for getters to end up with a number, we could
call the getter stripped from the (optional) number, so that
@Deconstruct Object x1,y1=foo.getPoint();
would compile to
var _fresh=foo.getPoint();
var x1=_fresh.x();
var y1=_fresh.y();

On Tue, 3 Oct 2023 at 20:36, guillaume LE LOUËT
> To view this discussion on the web visit https://groups.google.com/d/msgid/project-lombok/ba23ecde-4f69-4a66-b9f3-bbef39979eb2n%40googlegroups.com.

guillaume LE LOUËT

unread,
Oct 3, 2023, 3:24:00 PM10/3/23
to Project Lombok
Please don't make such a loose assumption. getters can end with a number, eg

@Getter

Object pos1;

creates the getter
 getPos1()

What can be done however is to specify a prefix/suffix to append to the fields, eg

@Deconstruct(prefix = "d_", suffix="_s") Object title, author=book;

would be replaced by
 var p_title_s=book.getTitle(), p_author_s=book.getAuthor();

This would also allow to avoid name clashes for comparison, typically

@Deconstruct(prefix = "b2_") Object title, author=book2;

would be replaced by
 var b2_title=book.getTitle(), b2_author=book.getAuthor();
So you could compare eg

Objects.equal(b1_title, b2_title);


Note that this is almost same as the one I proposed, except it's` b1_` instead of `b1. ` . However it is better since you don't need to modify the pojo.

Also I still believe that the specific a=a case could be used to alleviate the syntax on simple pojos.

Rawi

unread,
Oct 14, 2023, 10:35:23 AM10/14/23
to Project Lombok

I recommend beginning with a basic implementation first. Additional features, such as prefixes, can be added in later versions. I believe the syntax is quite solid. Perhaps adding curly braces around the object to be deconstructed could be considered. Alternatively, you could explore the possibility of introducing a dedicated type (e.g., "deconstruct") that would then resolve to var.

-
I have also taken a look into your branch. I noticed that you have written the test cases as regular unit tests. However, in Lombok, the tests work differently. All files located in the 'before' directory are transformed and compared to the expected output in the 'after' directories. The code is never actually executed, so it doesn't make sense to add assertions there.

guillaume LE LOUËT

unread,
Oct 14, 2023, 11:17:21 AM10/14/23
to Project Lombok
I already started an implementation with the prefix. Because they are necessary.
So far I'm good besides not able to set the type to "var", and not able to find the return type from the methods.

Thanks for you advice about tests, but they would be better on the issue rather than on the feature discussion ^^ I did not know they were not loaded.

guillaume LE LOUËT

unread,
Oct 14, 2023, 11:19:33 AM10/14/23
to Project Lombok
Actually, they are loaded, because they fail with a syntax error when I don't do the correct call.

Rawi

unread,
Oct 14, 2023, 12:51:29 PM10/14/23
to Project Lombok
The prefix is only required in some cases and the handle would provide value without it (e.g. in a for loop). I would skip it and all the other attributes in your draft for the first version. You can add it back later without changing the core features. Support for fluent getters is the only thing that I might add in the initial version.

To support old java versions you can use "lombok.var" (chainDots(node, "lombok", "var")) and the normal lombok handle will resolve the return type of the methods/use the naive var feature. You have to decrease the priority of your handler to run before "var" for that.

The test transformation invokes the compiler, if there is a syntax error this step fails.

guillaume LE LOUËT

unread,
Oct 14, 2023, 5:51:48 PM10/14/23
to Project Lombok
Yes, the class being linked does not mean it's class loaded. I was thinking of using class static call to make tests but that would only be used by classloader - so no point if it's only parsed.

Indeed, the prefix is only required in some cases. Which are common, like comparing fields 1-to-1. Anyhow I see no reason to not include them at first, since they may otherwise break the implementation. They are required in the long term, so implementation with them is mind is also required.
The fluent getters are also considered, just as important (with method prefix = "" . I tried null but it resolves to default on @interface so it was "get" :P ). Also boolean getter with method prefix "is".
I also added option for no camlcase on method and camlcase on variable, just because (this one is not important)

How would the lomlbok var feature handle the case ? Maybe I should use the same resolution of type, if possible (not sure it is). I guess I need to handle three cases : 1. class used defined elsewhere, so I can ClassLoader.forName(field.type.getName()) and then lookup the methods with no arg and non-Void return type. 2. defined in compilation unit using javac 3. defined in compilation unit using eclipsec
I tried

variable.type = (JavacHandlerUtil.chainDots(varNode, "lombok", "var")).type;

and I got
> Object editionDate = mybook.getEditionDate();
So it remained  Object instead of lombok.var .

Rawi

unread,
Oct 15, 2023, 7:18:18 AM10/15/23
to Project Lombok
I completly overlooked the fact that we need to address the boolean case in mixed deconstructions, such as in "var name, enabled = job;". Without resoution we do not know that we have to call "getName()" and "isEnabled()". Even moving this into the annotation only helps if there are exclusively boolean fields. Especially the eclipse part will be way more complicated with resoultion and I'm uncertain whether the benefits gained from this feature are worth the increased effort and maintenance costs it would entail.

I believe we should accommodate two primary getter styles: 'get/is' and 'fluent.' We don't need to include support for unconventional prefixes; instead, we can utilize an enum to specify the supported styles.

When implementing new features, it's important to keep in mind that we won't be able to easily remove them after a release. Many developers use Lombok, so the design should be well-considered and as clear and understandable as possible.

guillaume LE LOUËT

unread,
Oct 15, 2023, 8:17:01 AM10/15/23
to Project Lombok

yes,  "is/get/fluent" needs to be done at the annotation level, that's why I added method prefix.
Hence the three lines for the same book :

Book mybook = new Book("author0", "bookname0", new Date(), true);

@Onstruct

Object author, editionDate = mybook;

@Onstruct(methodPre = "")

Object name = mybook;

@Onstruct(methodPre = "is")

Object purchasable = mybook;

Maybe this makes the feature unusable ?

is there a way to resolve the class ?


And yes, we need to think before adding a feature. devil lies in details.

guillaume LE LOUËT

unread,
Oct 15, 2023, 9:46:39 AM10/15/23
to Project Lombok
I added styles as "SourceType" enum.

@Onstruct

Object author// var author = mybook.getAuthor()

, editionDate = mybook;// var editionDate = mybook.getEditionDate()

@Onstruct(source=SourceType.FLUENT)

Object name = mybook;// var name = mybook.name()

@Onstruct(source=SourceType.BOOL)

Object purchasable = mybook;// var purchasable = mybook.isPurchasable()


They can be overwritten :

@Onstruct(source = SourceType.FLUENT, methodCml = Cml.CML)

Object name=mybook; // var name = mybook.Name()

@Onstruct(source = SourceType.FLUENT, methodPre = "make", suf = "_2")

Object name=mybook; // var name_2 = mybook.makename()



Still does not work with the type :

    [junit] Object author = // var author = mybook.getAuthor()
    [junit] mybook.getAuthor();
    [junit] Object editionDate = mybook.getEditionDate();// var editionDate = mybook.getEditionDate()
    [junit] Object name = mybook.name();// var name = mybook.name()
    [junit] Object purchasable = mybook.isPurchasable();// var purchasable = mybook.isPurchasable()


    [junit] Object name = mybook.Name(); // var name = mybook.Name()
    [junit] Object name_2 = mybook.makename(); // var name_2 = mybook.makename()

Victor Williams Stafusa da Silva

unread,
Oct 15, 2023, 10:36:10 PM10/15/23
to project...@googlegroups.com
I would post this in the github feature request thread, but I guess that here is a better option since there are more people listening:

Well, PHP has a function called `explode`. Lombok for a long time featured `@Wither` instead of `@With`.

Anyway, I asked ChatGPT to give names and this is what it suggested:
`@BeanToVariables`, `@BeanPropertyMapping`, `@BeanToFields`, `@InjectBeanValues`, `@BeanPropertyBinding`, `@BeanPropertyCopy`, `@BeanValueAssignment`, `@MapBeanToVars`, `@BeanPropertyTransfer`, `@PopulateFieldsFromBean`.

Then I asked it to redo that avoiding the word "bean". It gave me this: `@ObjectPropertyMapping`, `@PropertyAssignment`, `@ValueToObject`, `@ObjectValueTransfer`, `@PopulateFields`, `@PropertyMapper`, `@PropertyToVariables`, `@PropertyCopy`, `@ValueAssignment`, `@ObjectValueInjection`.

None of those suggestions seems to be a good fit, but they point at some direction and give some ideas and inspiration. So, I came myself with those ideas: `@PopulateVariables`, `@Populate`, `@MultiAssign`, `@GetAll`, `@MultiGet`, `@PropertiesToVariables`, `@ObjectToVars`, `@AssignFromGetters`, `@AssignAll` and `@Vars`.

guillaume LE LOUËT

unread,
Oct 16, 2023, 3:06:09 AM10/16/23
to Project Lombok
First, I ignore the part about chatGPT since its value is random.

Then, yes the issue on github is about my specific implementation - this topic is about feature discussion. Other people can make other implementations.

I think Explode, or Populate, are good. IMO criterions are :
  1. should clearly indicate what is done : create variables from getters.
  2. must be a verb (the annotation implies an immediate action)
  3. must not conflict with another notion in lømbok
  4. must not conflict with a function in java or another language to avoid making the annotation ambiguous
  5. must be short (we're here to reduce boilerplate after all !!)
Maybe then other proposal could be "Acquire" , "Resolve", "Fetch" (though this one is close to other notions like DB access), "Expand"
We can then find the synonym with the most likes.

Reinier Zwitserloot

unread,
Nov 16, 2023, 2:48:19 AM11/16/23
to Project Lombok
Vito, it has to be _syntactically_ valid java, lombok doesn't run until after parse. It can be semantically invalid (e.g. `String x = 5;` is syntactically fine, but semantically bizarre), but has to parse.

And `var {x, y, z} = foo;` isn't syntactically valid. There's nothing we can do, and if you want that, you're looking for a different project or language, not lombok.

The few alternatives I've seen in this very long thread aren't enticing. Sticking names in string constants is not acceptable, so `@Deconstruct("getter1", "getter2")` is no good.

You keep saying: "I just want some light syntax sugar". This isn't light, and what you want simply isn't possible.

Marco, this:

```
(foo, bar, baz) -> expr;
```

Is very creative but is probably a dead end too, for unfortunate reasons: Lambdas already are very very complicated type-resolution wise, we don't want the considerable maintenance burden. Lombok is moving towards being agent-based for all cases (because of OpenJDK's backwards incompatible crusade to kill off annotation processors, very annoying), so in theory we can do all of this stuff, but maintainability is important, and this [A] would be hard, and [B] seems on its face confusing. The point of lombok is generally that even folks who aren't aware of lombok's featureset stand a reasonable chance of figuring out what lombokized code does. `@Getter` is clear enough. So is zvar {prop1, prop2} = foo;` possibly - but that isn't syntactically valid, so not possible. A lambda is too misleading here. But, I grant you, that's closer than anything else in this thread!

To All,

I'm pulling the plug on this. We're not doing this: Soonish java will have deconstruction and that seems to address this case well enough that it's not worth the burden for lombok to jump the gun especially with an implementation that has maintenance or syntax baggage, which this likely will. In some future java you can probably do:

```
p with {
  System.out.println(firstName); // this is p.getFirstName()
  System.out.println(birthDate); // that's p.getBirthDate()
}
```
Reply all
Reply to author
Forward
0 new messages