Pattern assignment should work in all of the following contexts:

 - Assignment statements: P = e
 - foreach-loops: for (P : e) { ... }
 - (optional) try-with-resources: try (P = e) { ... }
 - (optional) method formals: void m(Point(var x, var y) p) { ... }
 - (optional) lambda formals: (Point(var x, var y) p) -> { ... }

It is easy (and normal) here, I think, to be a little confused. At the beginning I was thinking "boy inference of lambdas with target typing AND patterns is gonna make javac cry" - but I think the key to a lot of these is that whenever you see a pattern declaration, it's like if you had an _explicit_ type. So, while there might be magic at runtime to decompose the value into the binding variables, from a static perspective, typing the lambda:

Point(var x, var y) -> distance(x, y)

is no different than typing this:

Point p -> distance(p.x, p.y)


Exactly; think of it as a syntactic desugaring

    Point(var x, var y) p -> e

to

    Point p -> { Point(var x, var y) = p; return e; }

My intent was actually to require the top-level variable (p) in lambda/method formals, which emphasizes this.

Crucially, the pattern provides an explicit type (including generic type arguments, at least initially).

From here I guess the next step would be, for a lambda like this:

Box<String>(String s) -> ...

to avoid the outer `<String>` - but I'm not sure about that step. I think if we treat a pattern as a replacement for an explicit type, things works nicely and there's a simple user model to explain. If we make it too magic, it could be more concise, but also more confusing.


Agree.

I was thinking that maybe another way to get at that is by using unchecked exceptions - e.g. if pattern failure raised a well-known unchecked exception, then users could have a chance (if they want) at looking as to why things failed.

try {
   P p = e;
   ...
} catch (...)

The problem with this though is that the handler code is very distant from where the failure has happened (unlike in let/else).


Not only that, but if the exception is unchecked, it is really not obvious that the match is even partial.  I like that we require an intrinsically conditional control flow construct (instanceof, switch, catch) for partial patterns, and only allow total patterns in things that "look total".

And we can't really do:

P p;
try {
  p = e;
} catch (...)

Because the proposed pattern assignment doesn't support some form of blank declaration - e.g. a way to say:

Point(int x, int y);
if (...) {
    // assign pattern from here
} else {
   // assign pattern from here
}

Is this something we view as a limitation?


It's a glass which is either P% full or (100-P)% empty :)  But I would much rather drive towards making what you wrote legal, in some way, rather than make assignment partial in a less-than-fully-transparent way.

We've already encountered another place where we might want bind-to-existing: composition of deconstructors/patterns.  Suppose:

    class A {
        int a;

        deconstructor(int a) { a = this.a; }
    }

    class B extends A {
        int b;

       deconstructor(int a, int b) {
*           super(a);*
           b = this.b;
       }
   }

If we can't invoke another pattern with bind-to-one-of-my-bindings, then we'd have to write something like:

       deconstructor(int a, int b) {
           super(var aa) = this;
           a = aa;
           b = this.b;
       }

While this is not the worst thing in the world, it will surely be a persistent irritation.   So *some* way to say "bind to this variable" possibly under some restrictions (DU?) seems desirable. If we had that, then your if-else would do the trick:

    int x, y;  // blank locals, therefore DU
    if (!(target instanceof Point(__my x, __my y)) {
        x = y = 0;
    }




Reply via email to