At 5:43 PM -0500 28/2/03, CBFalconer wrote:
Grant Jacobs wrote:
At 10:10 AM -0800 28/2/03, Russell Whitaker wrote:
On Fri, 28 Feb 2003, CBFalconer wrote:
Grant Jacobs wrote:
.. snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Thanks, this is exactly what I meant. I need to do a little testing: it may be that --automake not "drilling down" the units or some other silly thing is confusing me. I'll get back once I'm done.
Note that its not the uses that does the export (I never meant to suggest or imply that!), but the interface. Uses only does an import. If the uses is placed in the interface, the interface of that unit gets inherited/propagated/call-it-what-you-will.
Let's try a little analogy:
PROCEDURE foo;
PROCEDURE bar; PROCEDURE baz; BEGIN END; (* baz *) BEGIN END; (* bar *) BEGIN (* baz is totally invisible here *) END; (* foo *)
(* If the earlier bar were visibile, we couldn't *) PROCEDURE bar; BEGIN END;
BEGIN (* baz is invisible here *) (* bar is locally defined, nothing to do with foo *) END.
Now think about the name conflicts that can arise if everything is visible everywhere. Consider foo ensconced in a separate module, which in turn uses a module holding bar.
The point is to enable break up into separate compilations without causing inexplicable errors.
Firstly, you're "cheating" in that you're using nested procedures and I'm sure you realise units and modules aren't designed to be tied to procedures and functions so this e.g. is purely academic from my point of view. You can't create the equivalent of nested procedures with units or modules, as that would require putting the uses inside the outer procedure (i.e. tying the unit to the procedure), e.g. you can't do:
procedure foo;
uses barthingy;
begin end; { foo }
Units/modules are tied to other units/modules, not to procedures or functions, at least in the implementations I know of! (And even if you could, you'd still have the issues I discuss below).
So... while this example is interesting as its impossible to directly implement in units/modules, its not a practical problem to me. It *is* an interesting limitation of units/modules, though.
Secondly, I never meant to imply everything is visible everywhere nor that it ought to be. Obviously, what you expose depends on the application at hand. On the note of hiding/exposing, a little philosophy FYI (at the risk teaching grandmother suck eggs, so to speak):
If a unit doesn't want something to be visible to code that imports the unit, the programmer simply doesn't place that thing in the interface section of the unit. Done. To hide procs./fns. from other procs./fns. in the same implementation unit, used nesting.
The importing units can choose to import all, some or none of the things offered up for import by any unit it uses, assuming there is a mechanism to selectively import items (obviously importing nothing of something you use doesn't make sense, but it *is* conceptually an option!).
This places more control in the hands of the code using units. In the procedure-based code, higher levels have no option but to accept what is defined at lower levels. If two different units offered bar(), higher-level code could choose which unit to import bar() from. There is no way to achieve this with procedures/functions; only one bar() could be offered.
So it really is a difference in philosophy. Should the power be with the lower levels to dictate the higher levels or vice versa? Should lower levels insist only one bar() is available, or can several alternatives be offered and one chosen by the importing code? You can argue for both in different cases.
What you seem to want is for units/modules to be able to choose to dictate what units/modules can use them (this is in effect what nesting does). We could come up with a construct like 'export bar only to foothingies, specialthingies;'. [This isn't a suggestion, Frank!! :-)] Thought of in a larger scale, this blocks re-use of lower level code as you'd be forever editing import restrictions in the lower-level units. That said, there probably is the odd case where it'd be useful.
The only way to make your example work under current schemes (as I understand them) is to move the inner bar outside foo; as I was saying units/modules are not designed for nested procedures. But if you accept this you can make it work (an equally academic example back to you! :-) ):
unit innerbarthingy;
interface
procedure bar;
implementation
procedure bar;
procedure baz; begin end; { baz }
begin end; { bar }
end.
unit foothingy;
interface
{ export only foo } procedure foo; begin end;
implementation
{ get the "inner" bar } uses innerbarthingy;
procedure foo;
begin end; { foo }
end.
unit barthingy;
interface
procedure bar;
implementation
procedure bar; begin end; { bar }
end.
unit getthingies;
interface
implementation
{ get foo() from foothingy and bar() from barthingy } uses foothingy, barthingy;
end.
The only "danger" is that the importing code needs to take responsibility for importing the right things (e.g. that we don't import bar() from innerbarthingy). Which comes back to the philosophy question again. With power comes responsibility...
There are a long list of other issues tied with this, but I'd rather get back to solving my practical problems...! If you want to ruminate on your own, you could consider (a) that most modern OOP languages share this issue in various ways (b) how hierarchial naming schemes (e.g. getthingies.bar vs. getthingies.foo.bar) figure in the mix, (c) polymorphism... and, err, I've got work to do...! Its fun mulling over all this, though.
BTW, my original questions weren't "how to program" but rather how does this particular implementation (GPC) work, esp. as the docs are are a tad thin.
Cheers,
Grant