For completeness, since Remi aimed at listing all alternatives, I think we should not forget Object algebras: https://www.cs.utexas.edu/~wcook/Drafts/2012/ecoop2012.pdf
My experience has been that this pattern is good for observing data structures (transforming, pretty-printing, etc), but it's usually hard to implement, say, equals() through it. On Tue, Mar 28, 2017 at 12:40 AM John Rose <[email protected]> wrote: > On Mar 18, 2017, at 3:32 PM, [email protected] 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 > -- Andrey Breslav Project Lead of Kotlin JetBrains http://kotlinlang.org/ The Drive to Develop
