September 22, 2014
On Monday, 22 September 2014 at 09:17:16 UTC, Walter Bright wrote:
> It is depth first. It starts at the innermost scope, which is the current scope. Somehow, we don't seem to be talking the same language :-(
>

Depth first in the sense, go from the inner to the outer scope and look for local symbols. If that fails, go from the inner to the outer and look for imported symbols.
September 22, 2014
On 09/22/2014 10:27 PM, deadalnix wrote:
> On Monday, 22 September 2014 at 09:17:16 UTC, Walter Bright wrote:
>> It is depth first. It starts at the innermost scope, which is the
>> current scope. Somehow, we don't seem to be talking the same language :-(
>>
>
> Depth first in the sense, go from the inner to the outer scope and look
> for local symbols. If that fails, go from the inner to the outer and
> look for imported symbols.

That sounds almost right, but it still suffers from hijacking issues, because more nested (non-explicitly!) imported identifiers would hide less nested ones.

What about:

Go from nested to outer scopes and look for local symbols. If that fails, look up the symbol simultaneously in all modules that are imported in scopes enclosing the lookup.

I currently think this would be the most sane behaviour for imports. It would need to be determined what to do about template mixins.
September 22, 2014
On 9/22/2014 1:27 PM, deadalnix wrote:
> On Monday, 22 September 2014 at 09:17:16 UTC, Walter Bright wrote:
>> It is depth first. It starts at the innermost scope, which is the current
>> scope. Somehow, we don't seem to be talking the same language :-(
>>
>
> Depth first in the sense, go from the inner to the outer scope and look for
> local symbols. If that fails, go from the inner to the outer and look for
> imported symbols.

I would find that to be surprising behavior, as I'd expect inner declarations to override outer ones.
September 22, 2014
On Mon, Sep 22, 2014 at 02:58:33PM -0700, Walter Bright via Digitalmars-d wrote:
> On 9/22/2014 1:27 PM, deadalnix wrote:
> >On Monday, 22 September 2014 at 09:17:16 UTC, Walter Bright wrote:
> >>It is depth first. It starts at the innermost scope, which is the current scope. Somehow, we don't seem to be talking the same language :-(
> >>
> >
> >Depth first in the sense, go from the inner to the outer scope and look for local symbols. If that fails, go from the inner to the outer and look for imported symbols.
> 
> I would find that to be surprising behavior, as I'd expect inner declarations to override outer ones.

I think what makes the situation under discussion stick out like a sore thumb, is the fact that a local import statement inserts symbols into the current inner scope "implicitly". That is to say, when you write:

	struct S {
		int x;
		void method(int y) {
			foreach (z; 0..10) {
				import somemodule;
			}
		}
	}

the statement "import somemodule" can potentially pull in arbitrary symbols into the inner scope, even though none of these symbols are explicitly named in the code. If we were to write, instead:

	struct S {
		int x;
		void method(int y) {
			foreach (z; 0..10) {
				import somemodule : x, y, z;
			}
		}
	}

then it would not be surprising that x, y, z, refer to the symbols from somemodule, rather than the loop variable, method parameter, or member variable.

However, in the former case, the user is left at the mercy of somemodule (keep in mind that this can be a 3rd party module over which the user has little control) what symbols will be introduced into the inner scope. If somemodule defines a symbol 'x', then it silently overrides S.x in the inner scope. But since 'x' is implicit from the import statement, a casual perusal of the code would give the wrong impression that 'x' refers to S.x, whereas it actually refers to somemodule.x.

This problem is intrinsically the same problem exhibited by shadowing of local variables:

	void func(int x) {
		int x;
		{
			int x;
			x++;
		}
	}

which is rejected by the compiler. Both result in code that is difficult to reason about and error-prone because of symbol ambiguity.

Arguably, we should reject such import statements if it would introduce this kind of symbol shadowing. But regardless of whether we decide to do that or not, it's becoming clear that unqualified local imports are a bad idea, and I, for one, will avoid using it for the above reasons. Instead, I'd recommend only using qualified local imports:

	void func(int x, int z) {
		import submodule : x, y;
		// I explicitly want submodule.x to shadow parameter x;
		// I also name submodule.y so that in the event that
		// submodule actually contains a symbol 'z', it does not
		// accidentally shadow parameter z.
	}

leaving unqualified imports to the module global scope. In an ideal world, the language would enforce this usage.


T

-- 
Life would be easier if I had the source code. -- YHL
September 23, 2014
Am Mon, 22 Sep 2014 00:05:09 -0700
schrieb Walter Bright <newshound2@digitalmars.com>:

> On 9/21/2014 11:44 PM, Marco Leise wrote:
> > But quite understandable that people expect them to be in the same scope, seeing as there is only one set of {}.
> 
> { } introduce a new nested scope, they do not extend an existing one.

Of course, but

void foo(int x)
{
	int y;
}

appears to the uninitiated as two variables that will only be
visible inside of foo. The notion of a sourrounding parameter
scope only comes up when you dig deep into the language. It
is almost safe to say, without reading the specs or the
compiler source one wouldn't know. (I didn't :) )

> > Adding some
> > shadowing warnings should deal with that, so that the earlier
> > example with `text` being hijacked somehow errors out.
> 
> Are how the current lookup rules work (for better or worse) clear to you or are they still mysterious?

Now that I understand that the parameter scope exists I
understand how the lookup happens.
I could say the same about the famous JavaScript WAT video.
Once you understand all the language rules it becomes sane.
That is exactly what a WAT is about: It is a logical
consequence of some hidden context inside the language that
yields totally unexpected results in innocent looking code.

-- 
Marco

September 23, 2014
On Tuesday, 23 September 2014 at 19:04:55 UTC, Marco Leise wrote:
> Of course, but
>
> void foo(int x)
> {
> 	int y;
> }
>
> appears to the uninitiated as two variables that will only be
> visible inside of foo. The notion of a sourrounding parameter
> scope only comes up when you dig deep into the language. It
> is almost safe to say, without reading the specs or the
> compiler source one wouldn't know. (I didn't :) )
> 

It is obvious when you think about it. Consider contract for
instance.

> Now that I understand that the parameter scope exists I
> understand how the lookup happens.
> I could say the same about the famous JavaScript WAT video.
> Once you understand all the language rules it becomes sane.
> That is exactly what a WAT is about: It is a logical
> consequence of some hidden context inside the language that
> yields totally unexpected results in innocent looking code.

Yes, there is obviously a reason. Nobody explicitly decided that
these construct would yield completely idiotic results. But they
do.
1 2 3 4 5
Next ›   Last »