Member Patterns -- the bikeshed

Brian Goetz brian.goetz at oracle.com
Mon Apr 1 15:42:39 UTC 2024


There's a lot here, so let me try to separate.

On 4/1/2024 11:07 AM, Attila Kelemen wrote:
> ### Sibling problem of comethods
>
> I would like to have this proposal explicitly address the relationship 
> with failure handling, because whatever syntax is chosen for pattern 
> matching, it can prohibit future failure handling syntaxes. Since 
> comethods can be interpreted as methods returning 
> `UNION{Success(values), Failure}`, where the `Failure` in pattern 
> matching has only a single possibility (i.e., it is non-descriptive). 
> While this is okay in some cases, it is quite limiting in others. Such 
> as: I can imagine a simple `JsonNode.toString` wanting to use the 
> pattern matching feature, but in this case it is worthwhile to return 
> additional information in case of failure (what the parser likely 
> already has available).

This is not really related to the "sibling problem", though; it is 
orthogonal.

The one-bit failure channel is indeed a potential limitation; when there 
is a complex nested pattern, it might be useful to be able to debug "why 
did this big pattern fail."   Not a problem for simple type and record 
patterns, but as we have more opportunities to compose patterns 
(imagine: matching an entire JSON document in one big pattern), 
debugging "why didn't it match" could benefit from more context.

So noted: when making syntactic choices surrounding failure, try not to 
foreclose on future extension that might carry additional 
failure-related information.

> Also, while pattern matching is proposed to be used in switches and 
> boolean conditions there is another use-case that comes up frequently: 
> Matching failure is catastrophic. In this case, I don't really want 
> the ceremony. And I would prefer to avoid alternative method names, if 
> a pattern matching is already available. So, for example the statement 
> (the syntax is not a proposal, just an simple example):
>
> ```
> jsonStr instanceof JsonNode.toString(var jsonNode);
> ```
>
> The above example would throw a `PatternMatchingException`, if the 
> pattern matching fails.

What you are looking for is the ability to combine the effects of a 
match (binding the components) with an assertion that the match must 
succeed, so that you can assert that any failures are "unexpected" and 
can be handled implicitly by the runtime.  We considered something like 
this under the guise of a `let` or `match` statement, which is on hold 
pending a more disciplined analysis of totality requirements for pattern 
bodies, and may come back after that.

Again, this is an orthogonal problem (and we are working hard to avoid 
loading the requirements up with "wish list" items, because otherwise 
we'll never ship anything.)

> ### Syntax
>
> All of the examples I have seen forces duplicating a lot of 
> information (return / parameter types, and the name of the method).
>
> Wouldn't it be simpler to just force the comethods to be physically 
> next to its pair?

The "with inverse" suggestion is one we explored (and something similar 
was explored in the "JMatch" exploration (Liu and Myers, various 
publications.))  It has a few serious problems:

  - It is not like anything else in the language, nor is it a mechanism 
that seems likely applicable to other situations, which have the effect 
of making it appear "nailed on the side".  We are used to members having 
stand-alone declarations.
  - Since not every pattern is going to be the dual of an actual method, 
we still need a full-blown syntax for declaring patterns, in which case 
this just becomes a "weird shorthand" for a common case.

(The same argument was in play in deciding against declaring an 
exhaustive group of patterns with a single header.)

> There was a brief mention of "single-abstract-pattern", but I don't 
> see how it is intended to be used in pattern matching. It seems to me 
> that it would require some additional syntax burden. Can you clarify 
> the idea?

Lambdas.  A SAM interface is one with a single abstract method, and we 
extract the function shape and use it as a target type for lambdas.  
Similarly, a SAP interface is one with a single abstract pattern.  We 
can play the same game, except the shape is (match candidate) -> { 
pattern body that binds declared bindings }. This allows APIs to be 
extend with patterns, such as a `match` method in streams:

       objects.stream().match(e -> e instanceof String s)....

where Stream::match takes a SAP interface with a single binding, such as:

     interface Matchy<T, U> {
         pattern(T that) p(U u);
     }
...
     // in Stream
     <U> Stream<U> match(Matchy<T,U> m);


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20240401/a2741b60/attachment-0001.htm>


More information about the amber-spec-observers mailing list