pattern references, lambdas

elias vasylenko eliasvasylenko at gmail.com
Thu Jan 10 14:56:30 UTC 2019


It occurred to me that the current work on hashing out a proposal for
patterns <https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html>
(including deconstructor, static, and instance forms) also may imply some
other new concepts.
- "pattern references" as a complement to method references.
- "patternal interfaces" as a complement to functional interfaces,
containing an instance-pattern declaration rather than a method declaration.

I assume there is prior art to lean on, and that the concept of true
first-class patterns as a complement to first-class functions is nothing
novel to functional programmers ... so this is probably not new to many
people. I just wanted to put this out there while it's fresh in my mind and
hopefully see some public discussion on whether it's plausible for Java.

So, consider the following.

    items()
      .filter(SpecificItemType.class::isInstance)
      .map(SpecificItemType.class::cast)
      .forEach(itemHandler::doSomething);

Classic example of a test and extract being decomposed into two separate
steps when really we want to do it all at the same time. But how do we do
this with the current pattern proposal? The best we can do, I think, is
just to burden the caller with a little more responsibility to manage the
plumbing.

    items()
      .flatMap(i -> i instanceof SpecificItemType s ? Stream.of(s) :
Stream.empty())
      .forEach(itemHandler::doSomething);

This isn't too awful, but it's hardly an earth-shattering improvement. I
think it would be a terrible shame if we couldn't just do something like
this:

    items()
      .partialMap(SpecificItemType::instanceof)
      .forEach(itemHandler::doSomething);

Please excuse the naming of the new method and the strange syntax. (The
`instanceof` is supposed to indicate a reference to the normal type test
pattern as opposed to e.g. a deconstructor pattern which presumably would
look something like `SpecificItemType::new`.)

So to facilitate this, we need to implement `partialMap`.

    public default <R> Stream<R> partialMap(Pattern<? super T, ? extends R>
pattern) {
      return flatMap(e -> e instanceof pattern.match(r) ? Stream.of(r) :
Stream.empty());
    }

Where `Pattern` is the archetypal "patternal interface", comparable to
`java.util.Function`.

    public interface Pattern<T, R> {
      __Pattern T t match(R r); // 't' is the item to be matched and 'r' is
the sole component
    }

And we may also have e.g.

    public interface BiPattern<T, R, S> {
      __Pattern T t match(R r, S s);
    }

So is this something that's been considered? Is any of it plausible?
Desirable?

Lambdas are a similar story I suppose. For example, say we want to filter
our items based on the type, then on the presence of some kind of optional
content, and also then finally extract that content:

    items()
      .partialMap(item (content) -> __let SpecificItemType(Optional(var
content)) = item)
      .forEach(contentHandler::doSomething);

Or with a void instance pattern (note lack of assignment):

    items()
      .partialMap(item (content) -> __let item.content(var content))
      .forEach(contentHandler::doSomething);

But that is just illustrative, I realise there are a few reasons those
examples could be problematic ...
- The weird `item (content)` thing (which looks like a method invocation)
because we need to name both the input `item` and the components `content`.
- The apparent redeclaration of `content` when we assign to it via the
pattern.
- Whether a pattern "matches" or not must be dependent on whether all
components (in this case the variable `content`) have been assigned when we
return. That means more legwork for the runtime to keep track of assignment
as opposed to e.g. statically determining DA of components at each return
location.

Well that's getting off topic and bikesheddy. I apologise that this email
is a bit stream-of-consciousness. As I said I just wanted to open up some
public discussion.


More information about the amber-dev mailing list