[patterns] Destructuring without matching?

Remi Forax forax at univ-mlv.fr
Fri Jul 7 19:59:51 UTC 2017


Hi Tagir,

----- Mail original -----
> De: "Tagir Valeev" <amaembo at gmail.com>
> À: amber-dev at openjdk.java.net
> Envoyé: Vendredi 7 Juillet 2017 17:21:28
> Objet: [patterns] Destructuring without matching?

> Hello!
> 
> Sometimes it's reasonable to deconstruct the object of known type. A
> classical example is Map.Entry. E.g. currently I can write:
> 
> // Map<String, Integer> wordCounts
> wordCounts.entrySet().stream()
>  .filter(e -> e.getValue() > 10)
>  .map(e -> e.getKey()+": "+e.getValue())
>  .forEach(System.out::println);
> 
> It's desired to refer by concrete name to key and value like this:
> 
> wordCounts.entrySet().stream()
>  .filter((_, count) -> count > 10)
>  .map((word, count) -> word+": "+count)
>  .forEach(System.out::println);
> 
> Of course such syntax is not possible. Reading pattern matching proposal I
> thought whether it could solve this somehow. I see these alternatives:
> 
> wordCounts.entrySet().stream()
>  .filter(e -> e matches Entry(_, count) && count > 10)
>  .map(e -> e matches Entry(word, count) ? word+": "+count : null)
>  .forEach(System.out::println);
> 
> Or
> 
> wordCounts.entrySet().stream()
>  .filter(e -> exprswitch(e) {case Entry(_, count) -> count > 10; default
> -> false;})
>  .map(e -> exprswitch(e) {case Entry(word, count) -> word+": "+count;
> default -> null;})
>  .forEach(System.out::println);
> 
> In the latter case default branch is redundant as we know that e is always
> an Entry. Probably compiler could figure out this as well and do not
> require default branch? In this case exprswitch will have only one branch
> and looks too verbose.

yes, the compiler can figure that you do not need a default branch.

But for what you want you do not need to match/switch on things, you just want destructuration.

I think it's better to have a new operator, let call it 'let' that does the same destructuration that the pattern matching does,

wordCounts.entrySet().stream()
  .filter(e -> let (_, count) = e; count > 10)
  .map(e -> let(word, count) = e; word+": "+count)
  .forEach(System.out::println);

and given that we already have a way to support string interpolation in the JDK (StringConcatFactory),
you can even add String interpolation in the mix,

wordCounts.entrySet().stream()
  .filter(e -> let (_, count) = e; count > 10)
  .map(e -> let (word, count) = e; ''$(word): $(count)'')
  .forEach(System.out::println);

(you need a way to said that a String is interpolated, i've chosen '' (quotequote) which is close to " and currently invalid)

and with values types (thus tuples), you can write it that way,

wordCounts.entrySet().stream()
  .map(e -> let (word, count) = e; (word, count))
  .filter((_, count) -> count > 10)
  .map((word, count) -> ''$(word): $(count)'')
  .forEach(System.out::println);

regards,
Rémi

> Tagir Valeev.


More information about the amber-dev mailing list