Re: symbol resolution (was:EuCOM : Attn Matt : String Return Value)
- Posted by Matt Lewis <matthewwalkerlewis at g?ail?com> Oct 12, 2007
- 869 views
Pete Lomax wrote: > > Matt Lewis wrote: > > > > I wonder if we're talking past each other? I'll adopt your example from > > above: > > > > }}} <eucode> > > app.ex > > include main.e main.e > > global Z > > include libAmisc.e > > if Z then > > include libA.e > > global Z > > include libAmisc.e > > if Z then > > ? Z -- back in app.ex > > </eucode> {{{ > > > > I was talking about how to resolve Z. I'd say that an unqualified Z in > > this instance should get you main:Z. > > No, I'm really not getting this. Blindly assuming it is always the first Z > just > seems completely wrong to me. If I was struggling with some function in libA, > and tried setting or printing Z, I'd be miffed enough when I finally found out > about the Z in main.e. What if (an extra level of nesting and) I then did an > "include libA as A" to force the issue, and it crapped on some previously > working > use of unqualified Z later on because of this rule? Just force the namespace > qualifier. It is not as if this case happens very often. Well, in the example I gave, I think the reference to Z in libAmisc.e can *only* refer to main.e. Consider that at the time it is parsed (when main.e includes it) there is only one Z in existence. The interpreter cannot possibly know about libA:Z, and has only one reason to doubt that it meant main:Z, which is that libAmisc.e does not include any file that has a Z declared. This is perfectly legal behavior, even if it is dangerous for exactly the reasons above. The proper thing for the author of libA to do would have been to put an "include libA.e" at the top of libAmisc.e, since it depends on having that file around. That way, if someone decides to include libAmisc.e, it will still behave as it is supposed to. If libA.e is included first (or only) then the include statement in libAmisc.e only serves to inform the interpreter about where it expects to get symbols. Before I get into the issue of masking (which I believe is subtly different from this eaxmple) I think it's important to reiterate the purpose of this family of enhancements: * Make it possible and easier to use multiple third party libraries. * Avoid certain namespace conflicts where there is a reasonable or obvious way to resolve the conflict. * Reduce or eliminate the need to have to edit third party libraries to resolve namespace conflicts. Obviously, it will be possible for someone to write a library such that it may conflict with other code (possibly another poorly written library). There's only so much that we can do inside the interpreter. Now, on to the masking issue:
-- app.ex include lib.e as lib global Z include sublib.e global Z include sublib.e as sublib ? Z -- #1 compile error ? lib:Z -- #2 lib:Z ? sublib:Z -- #3 sublib:Z
I'm not sure why someone would write code like this, but I'm sure that it's either been done already or will be done. Under 3.1, we could access either one by using a namespace in app.ex. I've proposed extending the namespace for lib.e to include sublib.e. I think this is useful, because it avoids requiring library users to understand the structure of, say, win32lib, and simply use one namespace for all of win32lib. However, in this case, we have to decide what to do when we encounter something like lib.e and sublib.e. We can use a namespace identifier to access sublib:Z, but if we try to use lib:Z, there are now two possibilities. After reading your priorities, and (IIRC) on of CChris' arguments, I believe that it's reasonable to assume that using lib:Z from app.ex means that, in fact, you want lib:Z and not sublib:Z. But what if there is no namespace identifier? In this case, it's probably correct to throw a compile error. Back to using a namespace. I'm also thinking that we should only allow the "top-level" symbol to mask other symbols. IOW, I wouldn't use a full priority table like you did, but only divide the symbols into two groups, the symbols in the explicitly namespaced file, and the symbols in all of its includes:
--app.ex include lib.e as lib include sub1.e global Z include sub2.e global Z include sub1.e as sub1 include sub2.e as sub2 ? Z -- #1 compile error ? lib:Z -- #2 compile error ? sub1:Z -- #3 sub1:Z ? sub2:Z -- #4 sub2:Z
Consider that if there were no mask, it would be impossible to reference sub1:Z. I believe that this behavior follows these rules: 1: Unambiguity and Least Surprise 2: Zero impact on legacy code 3: Minimal performance impact In fact, now that I think about it, the masking behavior described is required to achieve #2. Matt