On Mar 18, 2017, at 3:32 PM, fo...@univ-mlv.fr 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 kind.) 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