[patterns] Destructuring without matching?
Brian Goetz
brian.goetz at oracle.com
Fri Jul 7 15:34:51 UTC 2017
On 7/7/2017 11:21 AM, Tagir Valeev wrote:
> 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.
Not so fast! We know that its static type is Entry, but it might be
null. In which case it would be foolish to try to let it match
Entry(var w, var c) -- because once we cast to Entry, we'll NPE
immediately when we try to extract the word/count components. (See the
recent writeup on patterns and nullity on amber-spec-experts.) Without
nullity information in the type system, that pesky default branch is
actually needed.
More information about the amber-dev
mailing list