Grant Jacobs wrote:
At 8:33 AM +0100 1/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
[snip]
You want to distinguish known and unknown identifiers in the error messages to make things clearer (?)
unknown identifier `%s'
type name expected, `%s' given
?
Yup. Such a major change, huh ;-) Being pedantic, I'd add 'unknown identifier' after the comma in the second line too:
type name expected, unknown identifier `%s' given
Perhaps I wasn't clear -- the second line is for the case where the identifier is known, but not a type name (but perhaps a variable).
Yes, that's why GPC has `export Foo = all' as an extension (but referring only to the current module's interface, not to re-exporting). With the proposed extension `export Foo = all (Bar, ...)', it would be possible to export all from the current interface and some selected items from imported modules (such as some types used, as in your example) which I'd consider more reasonable than just re-exporting everything.
I'd like to know more about this as it might let me do roughly what I wanted.
I've yet to implement it, so any details that may come up are still open. But AFAICS, it will mean:
- All declarations of the current interface are exported.
- If a name from the current interface occurs within the parentheses with renaming, the renaming is applied. E.g.:
export Foo = all (Bar => Baz);
var Bar, Qux: Integer;
would export Bar as Baz and Qux as Qux.
- If a name from the current interface occurs within the parentheses without renaming, this is redundant (maybe should be an error?).
- Any other name occuring within the parentheses must come from something imported in the interface, and is re-exported again. (With or without renaming.)
I think if you want to re-export selected items from imported units (say, an occasional type or constant), this would fit quite nicely. If, however, you routinely want to re-export everything, it will still be clumsy.
added an 'export all' of a sub module (which I think you were mentioning the other day), they'd pretty much be exact alternatives. (Export is still in the docs as 'not yet implemented!)
Don't you mean `exports' (which is, AFIAK, for BP compatible "libraries", not to be confused with `export' for EP modules)?
No. This sort of thing does make this kind of discussion confusing :-) I'm talking about modules in that paragraph (a la EP) and hence 'export'. Confused? ;-) (You've already answered part of this above.)
I meant your reference to the docs. `exports' is listed as not yet implemented (which is true), but where does it say that `export' is not yet implemented?
Read the *gist* of what I am writing more that my code which is just an incomplete skeleton. But this is all getting a bit long in the tooth -- ?
Sorry, I don't think I can do you this favour. I've just seen far too many cases of people making vague descriptions and incomplete examples, expecting us to second-guess what they really mean (which often turned out way off). Not your fault, sure. But I've given up on this, and prefer to reply to what's actually said.
By the way, to do this properly AFAIK, you need two places for uses per unit.
As I've said elsewhere and an earlier code example showed, there are two locations for uses under this approach. One in the interface section and one in the implementation section.
That's possible with both EP modules and BP units.
The latter imports everything and the kitchen sink to implement its stuff, with no effect on other units. The former, if done properly, only imports (and hence propagates) just those things needed to satisfy the declaration part of the interface. Those are "coincidently" just the things that any importing unit must acquire to use the interface.
Not necessarily. Consider:
Unit1:
const Foo = 42;
Unit2:
uses Unit1;
type Bar = array [1 .. Foo] of Integer;
or:
Unit1:
type Foo = [...];
Unit2:
uses Unit1;
type Bar = record Baz: Foo end;
In both cases (and many more), a program can use Unit2 without using Unit1 (explicitly or implicitly) and have full access to Unit2's definitions.
(2) two parallel uses, one in the interface section and one in the implementation section rather than having one uses work across the interface/implementation boundary
I find this a strange idea -- the implementation gets all the declarations from its interface part, but shouldn't get its imports, but since you don't want to discuss this, we can only agree to disagree.
(3) ideally a means of only importing a subset of an interface so that the users can choose not to propagate the whole of the lower-level interfaces.
That's already possible with `import only' (note: for importing, not for automatic re-exporting).
By way of example, think about what happens if we shift fnames to another unit. Under what you've told me (assuming I'm reading you right), all units which need fnames are now going to have to be edited and re-compiled to import the new unit that now holds fnames. By contrast under the inherited model if fnames is exported by FileOps, units importing FileOps need not be changed; the inheritance looks after the dependencies. Only units immediately "above" where fnames is exported need to be updated. Higher levels will get the type regardless via inheritance. The higher levels are still getting exactly the same items, its just they no longer need to maintain the dependencies of the lower-level units.
Yes, in this example there would be an advantage. We could probably argue about the importance of such cases. Generally, when you shift around definitions across units mid-way in a large project, you should be prepared to have some `uses' lines changed. -- In this example there are probably units that do use FNameUnit directly and do not use FileOps, and those still have to be amended. (If there are no such units, then why separate FNameUnit and FileOps at all? Then you could just move the definition of FNames into FileOps where it would then seem to belong.)
In principle another way of doing this would be to have interfaces export not just the procedure and function identifiers but also the types found in the procedure/function declarations.
Perhaps nitpicking again: They do export the types, but not the type names. In my 2nd example above, the compiled interface of Unit2 would contain a description of type Foo, but not the identifier Foo. I.e., to a program using Unit2, but not Unit1, it would appear like a nameless type, as if Unit2 had said: `type Bar = record Baz: [...] end;'. Maybe that's clear to you, I'm mentioning it just in case, trying to avoid further confusion.
Trouble is there may be the odd constant, etc. that the unit logically wants exported for higher-level use and you'd still need a mechanism to export these. So you might not be much better off.
Of course, you could create an aritifical subrange type just to have the constant mentioned, but that would really be a kludge ... ;-)
This would be a backend change. Peter suggested this to the GCC list some time ago, and it was rejected. (Or rather, he suggested something else, and someone turned turned his suggestion into this, and others flamed him for suggesting what he didn't and for a dozen of other things ...)-:
I am feeling a small bit of how Peter must have felt right now as a similar thing is happening. Some people, rather than trying to *understand* what I am driving at are presenting their own vision, pointing at their *own* vision, then saying "this is stupid" implying in the process this is what I suggested when in fact it *their* vision they are talking about... If they stopped at *thinking* (to themselves, of course!) "that is stupid"... therefore I perhaps don't really get what this guy is after... let's either look at this again or ask... I'd feel a hell of lot more welcome here.
Sorry if you feel this way. However, AFAICS, no-one is claiming that your idea is the same as our model or something like this. Your presented your model, and we showed ours, then we discussed the relative pros and cons of them. Isn't this a valid form of discussion?
Frank