Pattern Matching

John Rose john.r.rose at
Mon Mar 27 21:39:23 UTC 2017

On Mar 18, 2017, at 3:32 PM, forax at wrote:
> The main issue of the visitor is that you have to add the accept methods on the class of the hierarchy before being able to use the visitor, this is equivalent to be able to insert a method or a constant into a class, which is also equivalent to be able to inject the implementation of a not yet existing interface.

Good point.  Some form of post-facto interface injection (if we could figure
out the details, which is very hard) would presumably address this problem.

The issue of visitors and matchers is important because if we introduce
a new kind of class (data class, record class, whatever) with enhanced
pattern capabilities, we have basically one chance to define a universal
pattern match interface for that kind of class.  (We could add it in after
first release, but it's hard to add more than once.)

Here's a second point of similar weight:  The interface itself has parts
which are signature-polymorphic.  If you try to represent it as a classic
Java interface you can see that the polymorphism causes boxing:

interface Matchable<R extends Matchable.Result> {
  R match();

Whatever the API structure is for match patterns and results,
the result eventually has to deliver a tuple of extracted values.
But there is no good way (yet, until value types and any-generics)
to express the type of a tuple.  So we get List<Object>, etc.

The closest we can get to a tuple type in the JVM is an argument
list type, reified as a MethodType and accepted by a MethodHandle.
Therefore, I think a workable "Matchable" API can involve method
handles and be type-classified by MethodTypes (returning a
conventional void result).

As a first cut:

interface Matchable<MT extends MethodType<void, A...>> {
  boolean match(MethodHandle<MT> collector);
  MT matchType();

(The pattern part of the match is left out for clarity.
You can put it back in easily enough as another argument
to the "match" call.  Maybe match is overloaded by pattern

The type variable decorations are ill-defined and have to
be stripped out of the real code.

Second cut:

interface Matchable<MT extends MethodType<void, A…>> {
  BUF match();  // returns null on failure, buffered match-values on success
  <R> R matchExtract(BUF, MethodHandle<MethodType<R, A…>> collector);
  MT matchType();

The extract calls either look into the match-result buffer
for the required match components, or (as an optimization)
might look directly into the object fields, if it is safe to do so.

A third cut might break the mold completely (of a classic
interface) and present the Matchable API as a statically
linkable bundle of method handles, one bundle per match
API binding (i.e., per concrete class).  The bundle would
look like:

interface Extractor<T, BUF, MT extends MethodType<void, A…>>  {
  MethodHandle<BUF, T> matchHandle();  // null on failure, T or other value on success
  MethodHandle<?, T, BUF> componentHandle(int i);  // extract one of the A values
  MT matchType();
  Class<T> targetType();
  Class<BUF> bufferType();

You could omit the BUF type completely, but there is a big cost:
There is no way for the T object to deliver a tuple of types apart from
being willing at any moment to be the subject of an accessor call.
Those accessor calls will need in general to do redundant calculations
and are subject to race conditions which might make the match
disappear before the components were extracted.

The presence of the T type (alongside BUF) in the component
handles allows an object with immutable fields to deliver those
particular values by direct access, instead of copying them
through a buffer object.

The BUF type is secret to the implementation of T.  You can
use an extractor without knowing it except via a wildcard.

— John
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the amber-spec-experts mailing list