manifold-systems / manifold

Manifold is a Java compiler plugin, its features include Metaprogramming, Properties, Extension Methods, Operator Overloading, Templates, a Preprocessor, and more.
http://manifold.systems/
Apache License 2.0
2.43k stars 125 forks source link

[Feature] Add support for tuple destructuring #485

Open CC007 opened 1 year ago

CC007 commented 1 year ago

Is your feature request related to a problem? Please describe. Consider the following method:

auto returnTuple() {
    return "foo", 42, 13.37, (LocalDateTime.now());
}

When you want to use specific components of this tuple, it becomes quite verbose:

var tupleItems = returnTuple();

// You'll have to use the item1, item2, etc. to access the items
String s = tupleItems.item1;
int i = tupleItems.item2;
double d = tupleItems.item3;
LocalDateTime ldt = tupleItems.item4;
System.out.println("s = $s, i = $i, d = $d, ldt = $ldt");

// or directly, which makes the line a bit longer
System.out.println("s = ${tupleItems.item1}, i = ${tupleItems.item2}, d = ${tupleItems.item3}, ldt = ${tupleItems.item4}");

Describe the solution you'd like It would be nice if manifold supported tuple destructuring, like so:

var (s, i, d, ldt) = returnTuple();
System.out.println("s = $s, i = $i, d = $d, ldt = $ldt");

This would also be a nice feature for interacting with records, when you want to more formally define and document the data structure. Consider this:

/**
 * This describes a person
 * 
 * @param name name of person
 * @param age age of person
 */
record Person(
    String name, 
    int age
) {}

...

String doSomethingWithPerson(PersonSupplier personSupplier) {
    var (name, age) = personSupplier.getPerson();
    return "$name is $age years old";
}

In the case that the tuple/record has more items than the destructuring requests, the extra items should be ignored. Maybe _ could be used for explicitly ignoring/skipping certain elements during destructuring, like so:

var (s, _, d) = returnTuple();
System.out.println("s = $s, d = $d");

Describe alternatives you've considered The syntax could also be like this for when you don't want to use type inference:

(String s, int i, double d, LocalDateTime ldt) = returnTuple();

Destructuring classes in general would technically also be possible, but would be way more complex and therefore out of scope, due to having to decide which fields/methods to make available as items, inheritance and an unclear ordering of the items. Records are straight-forward: they have no inheritance, all items are specified as record parameters, in a specific order and they have a getter by default.

Additional context See documentation for tuple deconstructing/destructuring in other languages:

JEP for java 21 for destructuring records when using instanceof: https://openjdk.org/jeps/440 preview JEP for java 21 for unnamed patterns and variables: https://openjdk.org/jeps/443

PS: why are the extra brackets needed around LocalDateTime.now()? It doesn't compile without them.

rsmckinney commented 1 year ago

Hi @CC007. All great feature requests.

Regarding, the LocalDateTime.now() question, I'm not seeing that.

  auto returnTuple() {
    return "foo", 42, 13.37, LocalDateTime.now();
  }

Works fine for me, both in IJ editor & compilation.

CC007 commented 1 year ago

I'm getting this: afbeelding

So without brackets compiles alright, but the item isn't usable as item4.

It got compiled to this instead: afbeelding

As you can see, it is compiled to now instead item4.

rsmckinney commented 1 year ago

Right. That is intentional. Tuples infer labels from the expressions if possible. In this case the method name.

CC007 commented 1 year ago

It is a bit unintuitive though that item4 couldn't be used as well as an alias.

rsmckinney commented 1 year ago

It is a bit unintuitive though

Yep. But I think label inference tends to be more useful than not because:

  1. Code completion.
    I would think the vast majority of manifold users are developing with IJ / Android where the tuple items are at your fingertips.

  2. Explicit labels.
    Unless the labels are apparent from the expressions (refs to fields, locals, properties, etc.) direct labels are best.

  3. Low arity.
    Most tuples consist of two items. If you’re using tuples, you probably like labeling your two items explicitly, otherwise you’d probably prefer a ‘Pair’ class or the like. Just my take on it.

CC007 commented 1 year ago

Those are indeed all good reasons why label inference is intuitive to have. What I found unintuitive though was not that there is inference, but the lack of the item4. I'm a proponent of having both.

rsmckinney commented 1 year ago

Sure, that makes sense. Always have the itemN syntax regardless of the labels. I like it.

Would be interesting to make array operator work where a compile-time constant index expression would determine the type:

String item1 = tuple[0];
int item2 = tuple[1];

lol

CC007 commented 1 year ago

Agreed. Actually wanted to add that as a feature request too, but considering the type differences, I could see that being an issue.

CC007 commented 8 months ago

Any progress on implementing this feature?