[patterns] Scoping

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Fri Aug 4 07:23:53 UTC 2017


Hi Tagir,
I think that something similar to your opt 2 is more or less what the 
prototype does. So, when a binding is introduced, its name is in scope, 
but it could be either initialized, or uninitialized. Have you tried the 
implementation?

Maurizio


On 03/08/17 19:46, Tagir Valeev wrote:
> Hello!
>
> I was thinking about scoping problem of the variable declared inside
> pattern. It seems that the problem could be simplified if we think of it as
> two separate operations: declaration of uninitialized variable and variable
> assignment. Why it's simpler? Because, first, assignment already could be a
> part of expression, so nothing new here. And second, javac already performs
> a control-flow analysis to check whether a variable was initialized at
> particular use location.
>
> We can rewrite any "x matches Type t" expression in current Java via "(x
> instanceof Type && _true(t = (Type)x)" where _true is a helper method:
>
> static boolean _true(Object x) {return true;}
>
> And the only question to discuss is where to place an uninitialized
> declaration of t. I see two possible options:
> Opt1. Place it right before the current statement like
> Type t;
> if((x instanceof Type && _true(t = (Type)x)) {...}
>
> Opt2. Place it right before the current statement, but enclosing into the
> new scope (so it's not declared after current statement):
> {
>    Type t;
>    if((x instanceof Type && _true(t = (Type)x)) {...}
> }
>
> In any case you don't need complex rules about && or || or ?: or whatever.
> They are already covered by javac behavior. Some examples follow.
>
> // Test interfaces
>      interface Node {}
>      interface IntNode extends Node {int value();}
>
> // && example
> IntNode x;
> if((n instanceof IntNode && _true(x = (IntNode) n)) &&
>      x.value() > 5) { // ok
>      System.out.println(x); // ok
> } else {
>      System.out.println(x); // error -- not initialized
> }
>
> // ! and || example
> IntNode x;
> if(!(n instanceof IntNode && _true(x = (IntNode) n)) ||
>      x.value() > 5) { // negated pattern with || -- ok
>      System.out.println(x); // error -- not initialized
> } else {
>      System.out.println(x); // ok
> }
>
> // ?: example
> IntNode x;
> if(!(n instanceof IntNode && _true(x = (IntNode) n)) ?
>          x.value() > 5 : // error -- not initialized
>          x.value() < 0) { // ok
>
> }
>
> // return example
> IntNode x;
> if(!(n instanceof IntNode && _true(x = (IntNode) n))) return;
> System.out.println(x.value()); // ok if Opt1 is selected; cannot find
> symbol if Opt2 is selected
>
> // for example:
> IntNode x;
> for(int i = x.value(); // error -- not initialized
>       (n instanceof IntNode && _true(x = (IntNode) n));
>       i+=x.value()){ // ok
>      System.out.println(x.value()); // ok
>      n = next();
> }
> System.out.println(x.value()); // error -- not initialized if Opt1, cannot
> find symbol if Opt2
>
> This solution does not add non-contiguous scopes: scope is always
> contiguous, though variable is not initialized in some places. Note that
> this also fixes problem with shadowing quite reasonably: in both Opt1 and
> Opt2 cases if we have if(n matches IntNode x) {...} else {...} we cannot
> use x in else branch even if there's a field with the same name in the
> scope. The question is only whether we can continue using x after current
> statement, but to my opinion either solution could be good. Opt1 is less
> restrictive (see return example), but Opt2 is closer to what we have in
> Java now (e.g. when variable is declared in for initializer, it's not
> visible after for). Finally if we want to reuse the variable in several
> places within single statement, fine then. As long as type is the same we
> reuse the declaration and assign it in several points.
>
> What do you think?
>
> With best regards,
> Tagir Valeev



More information about the amber-dev mailing list