goran.hultgren@bluefish.se wrote:
without any neighbor modules. So ok, I need to do it explicitly, I ran the "self deepDeclareExternalRefs." from the explorer-thingy and then BAM, it added two neighbors:
#(Squeak Language Core)* #(Squeak Language Collections)*)
I am not sure why it didn't go all the way down to Streams... Even adding a reference to ReadStream didn't make it go there. It actually seems that these two modules are always neighbors - even with an empty method!
The modules Language Core and Language Collections import all their submodules, including ReadStream.
hopefully Squeak will "auto import" a module during compilation - at least if there is only one module active that does define that name. Henrik?
Right. This is what deepDeclareExternalRefs does. But automatic declaration is not really an option, your module dependencies really describe the "organizational logic" of your code, which isn't evident from the names you use.
For example, if your module uses Balloon3D heavily, the auto-declarer will add, say, dependencies on five or more submodules of Balloon3D instead of a single one to the Balloon3D module, which would be the logically right thing to do. (Balloon3D then ought to import its submodules so you only need to declare a single dependency on that module.)
An auto-declarer can never discover that this is the "correct" higher-level logic behind your code, because sometimes you may want to import exactly two of the B3D submodules instead of all of B3D or whatever.
- Imports are made on module level. We already have that - the neighbor
modules. I think. 2. If you type a direct reference in the code it would generate an automatic neighbor module reference on compile. It would of course barf if that module is not loaded (it doesn't have to be active, right?). 3. If you type a non direct reference like "EllipseMorph" then I would like it to:
- If there is already ONE and ONLY ONE neighbor module exporting
EllipseMorph then do nothing. All is fine. 2. If there are two or more neighbor modules exporting EllipseMorph then barf and ask user to make a direct reference using whatever syntactic means there are for that. :-) 3. If there are no neighbor modules exporting EllipseMorph, find it among all loaded modules and if there is ONLY ONE - add that module as a neighbor. Ask user for confirmation. This could be a Preference. 4. If there are more than one loaded module exporting EllipseMorph, ask which one should be added as a neighbor.
This sounds like a very good solution.
Now, as you noted, there still is the odd case when you need to specify in your code exaclty what module to pick a name from, e.g. where there is a name conflict (perhaps Component is more likely to have a conflict than EllipseMorph though!)
Like David S. noted (and Les did last fall), giving the full path inside your code is brittle. So I fully agree,
ellipse := Squeak Morphic Core Basic EllipseMorph new.
is not a great idea. That is why you can use aliases for external modules, e.g. you'd declare that your module depends on #(Squeak Morphic), and that this module should have the alias Morphic inside your module.
so in the dependencies (the "interface" of the module):
... externalModule: #(Squeak Morphic) alias: #Morphic ...
and in your code,
morph _ Morphic Component new
to distinguish it from the Component class in the super-meta-reflective programming code you also depend on:
... externalModule: #(Project FancyMetaProgramming) alias: #MetaSystem ...
model _ MetaSystem Component new
I think this is acceptable, even probably preferrable to other solutions IMHO.
On the same note, using prefixing, i.e. always using Morphic Component instead of just Component, probably leads to less brittle code, since you are always explicit and clear about where you are taking a name from. Just "blindly" using imported names from all over can lead to really confusing bugs.
If people don't want to have to declare aliases all the time, then the name of the last module in the path could be allowed as a default, ie. #(Squeak Morphic) would allow Morphic as an alias by default, but perhaps that is not worth the potential confusion.
But it sounded as Stephen wa talking about having "the old" references still point to the "old" name but the new ones (after having added the extra neighbor) point to the new name. Uahh! That sounds SCARY to me. How would I know what it points at by reading the code? Perhaps I misunderstood.
It would also cause recompilation to fail, so it wouldn't work in practice.
Unfortunately, an implicit reference will not work when loading the module into a fresh image (how would we know what module to bind it to? And, we don't want to prompt the user at load time). Implicitly binding based on the order of the modules would be tricky and obscure as well.
Yes, you couldn't be allowed a choice between different bindings for the same name, you'd always have to use the first one given by the order of your imports. Otherwise loading or recomp. would fail. Or each Squeaker loading your module would have to specify which binding you intended your code to have. ;-)
Lex Spoon wrote:
Modules @ #Morphic @ #Core @ #Basic @ #EllipseMorph
Now you can look up #@ and see what is happening, where before you had to lookup up #doesNotUnderstand:.
Here, it would translate to
model _ (Morphic @ #Component) new
which I think would be acceptable (but one must remember to use parentheses for this one). But beginners are likely to be confused by the # here, since they already are puzzled by having to use #SubclassName when creating new classes. So I don't think there is a "perfect" solution to this.
Stephen Pair wrote:
Regarding the issue of importing a new module that has a duplicate export of a symbol that's already being used...maybe the solution to this problem is to enhance what the compiler is producing. Rather than compile in references directly to an association, maybe we should compile in a reference to something a little more sophisticated...if we change the VM such that the #value method is actually sent (rather than make an assumption about having an association), this should be an easy thing to achieve (and it's arguably the right thing to do regardless).
This is propbably what Dan had in mind with the uppercase message mechanism. "Morphic Component" will actually send a message to the value of the Morphic binding, causing this lookup, like you suggest (if I understoood you correctly). And this with no special machinery. No recompilation needed if the binding of the alias Morphic changes.
Also, someone suggested that we automatically would compile in methods that would return the variable value, instead of using doesNotUnderstand: like now.
On another note, regarding the upper case convention, what happens for lowercase modules names? Like "People svp"? Would we need to make the people modules upppercase? Like "People Svp" or "People SVP"?
Note that this is just a special convention for accessing names defined in modules (which may be their submodules, or globals, but typically classes). So the message selector is identical to the accessed name. So if you would deviously force your module to define the global "myGlobal" (non-uppercased) then "YourModule myGlobal" would be the message to access it.
I actually just encountered such a situation when trying to load the ModuleTool...it requested version 0.0003 of the module code and wouldn't load because I had 0.0004...even though the tool seems to work fine when I modified the def file to require 0.0004.
Yeah, it is still waiting for someone to implement a real versioning system. <hint> But the warning dialog could be better I think.
Actually this is a meta-level check, to ensure that the module code will be able to understand the definition format used in the file. Any refinements to the very crude current check are warmly welcome.
Henrik
Well, whatever, perhaps I am "out bicycling" as we say in Sweden. :-)
Note to non-Swedish speakers: Göran's Swenglish is hilariously funny if you know the expression in question!