Wow, it's bikeshedding time!

Yep.  I've painted this particular shed several hundred times already, so I've been round the block on most of these.

Two points.

1. I worry about the asymmetry between pattern-with-args declaration
and invocation. See:

static pattern(int comp1, int comp2) foo(Type t, int param1, int param2) {...}

if(t instanceof foo(param1, param2)(int comp1, int comp2)) {...}

See, at the declaration, pattern parameters are declared after the
components while at invocation parameters are listed before the
components.

Yes.  Both ways have their problems; this is a pick-your-asymmetry. In the end, it felt that:

 - Putting the bindings near "pattern" and the input args near the method name was more consistent;  - At the use site, having the input args come first is important; if you squint, you could almost imagine that in

    case Map.withMapping(k)(var x)

the "Map.withMapping(k)" is the pattern, and (var x) is the binding list for it.

 - That a deconstructor is a degenerate case of a pattern (no name, no input arg list) strengthens the model.

Also, having a target parameter as a part of the normal parameter list
could be confusing. An alternative idea is to list everything at
declaration in the same order as at invocation site. For example:

static pattern Type foo(int param1, int param2) -> (int comp1, int comp2) {...}

Heh, I explored this one too.  This has the advantage of describing inputs and outputs more clearly, but doesn't really connect with anything else in the language.

// it's not strictly required to give a name for the target parameter;
it could be referenced inside the pattern body via a specific keyword,
probably even 'this'!

class Optional<T> {
     static<T> Optional<T> of(T t) { ... }
     static<T> pattern Optional<T> of() -> (T t) { ... }
     static<T> Optional<T> empty() { ... }
     static<T> pattern Optional<T> empty() -> () { ... }
}

Yes, I've agonized about this one too.   One of the lumps in the current proposal is that the target declares like an ordinary parameter, but it is pretty magic (and the compiler will require that it be present.)  I think calling it `this` is a little too weird, but maybe we could get over that.  Or we could have another magic name (target, that, matchee), and then we could move the type out of the arg list, such as:

    static<T> pattern<Optional<T>>(T t) of() { ... }

and then we (a) don't have the confusion between the target and an argument, and (b) the argument list really is the argument list, not one bigger than the argument list.  This seems like a workable possibility.

static pattern String regex(String regex) -> (String... groups) {...}

interface Map {
     pattern withMapping(K key) -> (V value);
}

class Class<T> {
     public pattern arrayClass() -> (Class<?> componentType) {...}
}

Another possibility is to use another type of brackets for pattern
parameters, let it be []

Been there too :)  I liked this because this is how TeX deals with optional parameters (there's a required and optional parameter list).  Most people I showed this one to hated it, though.  (Not many people are TeX-heads.)

Other syntactic options are possible, but in general, it looks more
natural to me to specify output components on the right side of
pattern parameters.

Of these, the one that seems most significant is finding a different way to name the target, so we can get the target out of the argument list.

2. effectively final expression

We would likely want to enforce the requirement that expressions used as input
arguments be _effectively final_, to eliminate any confusion about the timing
of when they are evaluated.

I'm not sure I understand what is 'effectively final expression'. We
know what is 'effectively final variable'. Is it possible, say, to
call a method at the pattern invocation, like `case
regex(buildRegex())(String s1, String s2) -> ...`?


An effectively final expression is one for which all the inputs are effectively final; this basically rules out method calls (until we do the cool constant folding stuff.)  We could narrow this to "effectively final variable" if we didn't want to go through that level of detail.

In my head (maybe not elsewhere), a pattern is like a lambda; the "code" is constant, and it is possibly parameterized by captured variables.  Imagine that there were a first-class type called Pattern.  (Well, there is, but I mean one that evaluates to a linguistic pattern, not a compiled regex.)  Then we could conceivably say (not suggesting this, even though Scala lets you):

    Pattern p = PatternFactory.randomPattern();
    ...

    switch (o) {
        case p: ...
    }

While being able to programmatically build patterns might be cool, this seems over the line.  (There's precendent; you can say `case V` today in an int switch, but V must be a compile-time constant.)  A pattern is like a lambda, and so is a pattern switch; you want the "body" of it to remain constant (among other reasons, so you can compile it efficiently, once) even if it is parameterized by some captured variables.

Given the argument above where everything to the left of the *second* open-paren is a pattern:

    case Map.withMapping(k)(var v):

we are saying that, within a given scope, the semantics of the switch are fixed and can be compiled eagerly or lazily and it won't make a difference to execution.

Reply via email to