New pattern matching doc

Brian Goetz brian.goetz at oracle.com
Fri Jan 8 21:20:07 UTC 2021


>
> You say you look in the static type of Foo, but I think that isn't 
> quite what you meant: rather, we look in the type corresponding to 
> whatever the static type of the object being switched on is, which may 
> or may not be Foo (your example doesn't say). Otherwise, I don't see 
> how we could possibly come up with the type Foo specifically from this 
> pattern-match use site.

Yes, I was a little fuzzy.  What I mean is:

  - If a pattern looks like IDENTIFIER(BINDINGS), then it is either a 
deconstruction pattern (in which case the identifier is the name of a 
class, and we look there for a deconstruction pattern), or an 
(instance?) pattern on the static type of the target.  We prefer the 
former to the latter, and expect collisions to be rare because of 
adherence to naming conventions.
  - If a pattern looks like Qualifier.IDENTIFIER(BINDINGS), then it is 
either a static or instance pattern in type Qualifier.

If we permit static imports of patterns, the first bullet would have to 
deal with these too.

> But this left me with another question, going back to the notion of 
> Map having an instance pattern taking a key and binding a value. How 
> do we invoke that pattern, if we don't know whether the static type of 
> the object being matched is indeed Map?

There's two things going on here: finding the pattern (saying Map.xxx is 
a help in doing so) and determining applicability.  And determining 
applicability may have two parts.  Suppose we have a pattern on Map 
called withMapping(k) and which has a target type of Map.  If I have an 
Object in hand, and try to match it:

     switch (obj) {
         case Map.withMapping(...):
     }

then in order to do the match I have to make TWO tests:
  - is obj instanceof Map
  - does the Map.withMapping() pattern match *this* map

The compiler will insert the first one if it needs to (and will report a 
compile error if the target types are not cast-convertible).

> Say I am using a JSON decoder, and I want to check several 
> possibilities: if it's an int, do one thing; a list, do another; a map 
> containing a "type" key I will handle specially; and all other maps 
> should be handled in some default way.
>
> Object o = JSON.decode(string);
> switch (o) {
>   case Integer x -> ...
>   case List<Object> items -> ...
>   case hasKey("type", String type) -> ...
>   case Map m -> ...
> }

In this hypothetical JSON API, the decode() method returns either an 
Integer, String, Boolean, List, or Map, right?   If so, the hasKey 
pattern will have, as its target type, Map, so

     case Map.hasKey("key", String value) -> ...

will work fine.  (Either it's a static pattern with target type Map, in 
which case we'll conjoin a type test against Map, or its an instance 
pattern on Map, so same.)

> but then I ask, what is the point of the instance pattern, if the 
> static pattern accomplishes the same things, and all it "costs" is 
> writing the `Map.` prefix? I wouldn't expect Map to actually expose an 
> instance pattern at all, once it writes this static pattern that does 
> the same thing. Do instance patterns have some speicial privileges or 
> something, that would make using them more convenient in some 
> situations, and outweigh the inconvenience of needing to have the 
> right static type already?

There are a lot of cases where a static pattern is fine, the main value 
of instance patterns over static patterns is mostly when you want to 
have abstract patterns / override patterns.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20210108/ae6a4206/attachment.htm>


More information about the amber-spec-experts mailing list