<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=utf-8">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-text-flowed" style="font-family: -moz-fixed;
      font-size: 14px;" lang="x-unicode">I would like to start the
      discussion surrounding pattern matching with some motivation and
      goals.  The design space here is enormous, and connected to so
      many other features, so bear with me as I try to linearize the
      story.  I've posted a general introductory document on
      possibilities for pattern matching in Java here:
      <br>
      <br>
      <a class="moz-txt-link-freetext"
        href="http://cr.openjdk.java.net/%7Ebriangoetz/amber/pattern-match.html">http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html</a>
      <br>
      <br>
      The first question is why, as in: Why is this feature needed in
      Java at all?
      <br>
      <br>
      I'll start with the obvious reasons (explicated in greater detail
      in the document): the common pattern of testing an input against
      multiple candidates not only suffers from boilerplate, but, more
      importantly, the tools we have for this give bugs too many places
      to hide, obfuscate the essential business logic, and introduce
      accidental ordering constraints that make programs slower than
      necessary.
      <br>
      <br>
      There are also some less obvious reasons why this makes sense.
      What's being proposed here are really two interrelated features
      here: better support for compositional test-and-extract
      (destructuring), and better support for handling
      do-exactly-one-of-these constructs.  While either could stand on
      its own, the two together are much stronger.  Of the two,
      destructuring is the richer concept.
      <br>
      <br>
      To illustrate why destructuring is so important, allow me to make
      a comparison to Lambda.  Adding the ability to pass behavior as
      data meant that we could build richer, more powerful libraries. 
      We could move responsibility for control flow from client code
      (external iteration) to libraries (internal iteration), enabling
      libraries to expose richer, higher-level operations. 
      Additionally, by exposing higher-level operations, libraries might
      not have to expose fiddly, state-dependent low-level operations
      like Iterator.hasNext() / next(), and therefore don't need to code
      iteration logic against being called in the wrong order or in the
      wrong state.
      <br>
      <br>
      Destructuring has a similar abstraction-raising characteristic.
      Consider the methods Class.isArray() and Class.getComponentType();
      the latter only returns a value if the former returns true. 
      Method pairs like this offer the worst of both worlds; the client
      has to make two calls, and has a chance to do it wrong -- and the
      library has to defend against the client getting it wrong.  These
      two methods really should be one operation, which simplifies both
      the client invocation and the library implementation:
      <br>
      <br>
      Â Â Â  if (c matches Class.arrayClass(Class<?> componentType))
      { ... }
      <br>
      <br>
      Further, decomposition (when done right), is compositional,
      enabling us to express a compound test+extract operation as a
      single operation, rather than as a sequence of operations, each of
      which can individually fail.  This brings client code more in line
      with how the problem statement is typically stated.
      <br>
      <br>
      Finally, this is a feature with depth; we can start with simple
      patterns and simple pattern-aware language constructs, and add
      more sophisticated kinds of patterns and more constructs that can
      use patterns as we go. 
      This in turn means a greater return on conceptual complexity.<br>
      <br>
      <br>
      If one looked only at the first few examples in the document, one
      might be tempted to ask "Why not 'just' do flow typing"?  Flow
      typing would eliminate the (irritating) need to cast something to
      X right after having done an instanceof test against X.  It seems
      like a cheap win (though would invariably put pressure on our
      handling of intersection types), but it just doesn't go very far
      -- essentially it eliminates a single increment of boilerplate and
      a single place where error can creep in.  This is not nothing, and
      it's a defensible choice, but it seems like it would be a missed
      opportunity.
      <br>
      <br>
      Similarly, one might ask "Why not 'just' do type switch?".  Again,
      this is a feature that seems merely "additive" rather than
      multiplicative; it again eliminates some boilerplate in repeated
      type tests, but it doesn't go much farther than that.
      <br>
      <br>
      <br>
      Which brings me around to a deeper observation, which we think is
      at the design center of this feature: destructuring is the natural
      dual of aggregation, and an obvious (and yet missing) component of
      object-oriented modeling.
      <br>
      <br>
      People often associate pattern-matching as being a "functional
      programming" feature.  But any language that supports aggregation
      (that is, all of them) also has to address how aggregates are
      going to be decomposed.  Scala moved the ball forward here by
      showing that having a destructuring operator ("unapply") on
      objects allows us to match and destructure objects in terms of
      what their constructor invocation looks like (regardless of their
      representation.)  This is not "OO borrows FP idea", this is "OO
      applies sensible programming concept in a natural OO way."
      <br>
      <br>
      That said, I think Scala didn't go far enough.  (No criticism
      intended; they moved the ball forward dramatically, there's just
      farther to go.)  For what are likely mostly accidental reasons,
      Scala only allows a single constructor pattern per class, even
      though constructors themselves can be overloaded.
      <br>
      <br>
      Again, without criticism: I'm guessing most of the motivation for
      Scala's pattern matching was drawn from the distinguished and
      well-behaved case of algebraic data types, not general objects. 
      So the limitation of a single unapply was not really a problem;
      algebraic data types don't go out of their way to hide or evolve
      their representation.  But we can apply destructuring to a wider
      range of targets, in a more OO way.  <br>
      <br>
      So, to put a stake in the ground, we believe that:
      <br>
      Â - destructuring is the dual of construction, and should be
      treated as a first-class construct;
      <br>
      Â - destructuring an object should be syntactically similar to how
      that object is constructed.
      <br>
      <br>
      From these principles, it follows that:
      <br>
      Â - if you can declare a ctor, you should be able to declare an
      instance dtor;
      <br>
      Â - instance dtors should support the same overloading and
      inheritance behaviors as ctors;
      <br>
      Â - if you can declare a static factory, you should be able to
      declare a static dtor.
      <br>
      <br>
      (The syntax of how dtors are declared is a topic for another day.)
      <br>
      <br>
      If an object is created with a constructor:
      <br>
      <br>
      Â Â Â  x = new Foo(a, b)
      <br>
      <br>
      ideally, it should be destructurable with an instance dtor:
      <br>
      <br>
      Â Â Â  if (x matches Foo(var a, var b))
      <br>
      <br>
      Note the syntactic similarity; this is not accidental.  Acquiring
      dtors isn't automatic (though data classes can automatically
      acquire both ctors and dtors), but it should be easy and
      straightforward to declare such a dtor.
      <br>
      <br>
      If dtors are overloaded, type information at the use site may be
      needed to disambiguate.  So if we provide dtors Foo(int y) and
      Foo(String y), then
      <br>
      <br>
      Â Â Â  if (x matches Foo(var y))
      <br>
      <br>
      is ambiguous, but by replacing the var-pattern with a an explicit
      type-test pattern, the compiler can properly select:
      <br>
      <br>
      Â Â Â  if (x matches Foo(int x))
      <br>
      <br>
      Similarly, if an object is created with a static factory:
      <br>
      <br>
      Â Â Â  x = Foo.of(a, b)
      <br>
      <br>
      it should be destructurable with static dtors:
      <br>
      <br>
      Â Â Â  if (x matches Foo.of(var a, var b))
      <br>
      <br>
      So, for example, since Optional has two factories:
      <br>
      <br>
      Â Â Â  x = Optional.of(y)
      <br>
      Â Â Â  x = Optional.empty()
      <br>
      <br>
      one can discriminate via:
      <br>
      <br>
      Â Â Â  case Optional.of(var y):
      <br>
      Â Â Â  case Optional.empty():
      <br>
      <br>
      <br>
      Why does this matter?  By way of example, we often prefer factory
      methods to constructors.  A factory has more flexibility than a
      ctor; it can return different subtypes in different conditions,
      and it can change its implementation over time to return different
      subtypes.  That a factory commits only to an abstract type:
      <br>
      <br>
      Â Â Â  static Foo redFoo() {
      <br>
      Â Â Â Â Â Â Â  return new MyPrivateFooImplementation();
      <br>
      Â Â Â  }
      <br>
      <br>
      is great for the implementor, but less so for the client -- once
      the caller gets a Foo from the factory, it has no way of asking
      "is this a red Foo?", unless we clutter the API with ad-hoc
      methods like isRedFoo().  This means that APIs with multiple
      factories have some bad choices: either live with the opacity,
      which can limit what the client can do (sometimes this is good,
      but sometimes it's not), add lots of new API points for
      destructuring, or expose the intermediate types.
      <br>
      <br>
      Having a complementary dtor allows the implementation to hide its
      details while still providing a degree of reversibility. Without
      venturing near the toxic syntax bikeshed:
      <br>
      <br>
      Â Â  static ... matcher ... Foo ... redFoo(...) { ... }
      <br>
      <br>
      the library author can render the factory reversible without
      exposing the implementation details.
      <br>
      <br>
      Â Â Â  x = redFoo();
      <br>
      Â Â Â  ...
      <br>
      <br>
      Â Â Â  if (x matches redFoo()) { ... }
      <br>
      <br>
      The implementation of redFoo() remains hidden, but because the
      dtor is part of the same library as the factory, it can still help
      a client reconstruct the object's state (or the parts that it
      wants to), without compromising the encapsulation.  (In just the
      last few weeks of thinking about this, once you start to see this
      pattern, you can't look at an API and not see it.)<br>
      <br>
      <br>
      Again, this is mostly something to file in the background and
      think about the consequences of -- until we start to circulate a
      JEP for pattern matching, this is still technically outside the
      Amber charter.  <br>
      <br>
      <br>
    </div>
  </body>
</html>