[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