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