Grant Jacobs wrote:
Well, I promised that I'd write up a brief account of what I really meant. Unfortunately I'm going to have to let it slip a little as I am extremely busy. Hope this rushed effort make its clearer, not more confusing! Looking at it briefly, if anything its redundant as all this seems to have already been covered, but all the same... I did promise more examples, which I have to admit I'm out of time for... maybe another week.
Anyway, on with the show.
Well, you've convinced me that the models are quite different. But even after reading your description, I must say that I prefer both the UCSD/BP model for its simplicity (see below) and the EP model for its explicit ways (import and export are separated, and you can import and (re-)export whatever you like, at the cost of some more typing).
Therefore I'm afraid, I don't think I will implement the other model, at least not in my free time. If you really need it, you could hire me to implement it -- I'd expect it to take a few weeks. Or you can try it yourself ...
I must also say that I'm a little angry at the inventors of these models (UCSD and Borland on the one side and Apple on the other, I suppose). Not only did they all depart from the standards efforts (I know EP wasn't finished at that time; but I also know that BP retreated from the commitee long before that, and even ignored the already existing classic Pascal standard; not sure about Apple's behaviour), but they implemented two models with the same syntax with are different, but not at first sight. Hardly anything could be more confusing.
A. Layout.
Basically identical to BP units:
[...]
The difference lies in the way the elements are used, not the syntax.
Because of this, I suspect if this were to be implemented, a compiler switch such as the one Chief suggested would be appropriate. No new keywords, Frank! ;-)
That's exactly the problem -- the same syntax for a different thing. A compiler switch for such a purpose is also not in line with the existing ones which should not modify the behaviour "substantially" (i.e., there are some to enable/disable some features, checks, or change the behaviour in details such as default output field width, but as you described, the two units models are fundamentally different, and a given source can possibly compile with both settings and yield different results which a user of the unit will notice; quite confusing -- just like your initial confusion when you found out about the differnce the hard way ...).
(OK, well, macros are an exception. So even if your unit model was implemented as, say, `unit with export Foo' (to write some silly combination of keywords), a switch `"-Dunit=unit with export"' (macro definition) would effectively make it possible to write `unit Foo' ...)
B. Independence of action of the interface and implementation sections
Firstly, let's clarify the flow of information of interface and implementation sections as this seems to have caused confusion amongst some people. Conventional units effectively work such that the declarations in the interface are imported into the implementation section of the same unit.
Here, interface exports all of its contents to other units. Its declarations are *checked* against those of the implementation section.
I suppose you mean only routines here. Are they only checked, or do they behave like `forward' declarations, i.e. can you do:
unit Foo;
interface
procedure Foo;
implementation
procedure Bar; begin Foo end;
procedure Foo; begin end;
end.
For types, constants and variables there is nothing to check, since their declarations are not repeated in the implementation ... or are they? -- Well, I think I still don't quite understand. What about the following (rather common) case:
unit Foo;
interface
type t = record [...] end;
procedure Foo (var p: t);
implementation
procedure Foo (var p:t); var Temp: t; begin end;
end.
Does this work? If so, does the implementation get t from the interface, or is it only available within the definition of Foo.
Or do you have to declare t again in the implementation (or import it explicitly)?
In any case, I see some difficulties when trying to implement it. First, one would have to write a mechanism to only get the routine declarations (depending on the answers above, either as forward declarations, or in a special state that doesn't exist currently: must be checked, but cannot be called yet).
Secondly, a compiled interface (GPI) must distinguish between here-declared and re-exported routines (only the former should get to the implementation in the way described IIUIC).
In case type definitions etc. have to be repeated, that adds more overhead -- not only for the programmer (IMHO), also for the compiler. Currently there's no code to check type definitions for equality (because that's not needed in regular Pascal; only type compatibility matters, and two distinct structures are never compatible, even if they look the same). So this code would have to be written from scratch. Same for variables and constants ...
[...]
Everything in the implementation section is private.
At least here it agrees with BP units. ;-)
The interface section will, of course, have to import the units needed to satisfy the declaration part of the interface. This has the effect that users of the current units get these dependencies "for free". This can work well, provided that developers of lower-level units don't place a lot of things into the interfaces that really aren't needed to be there - which they ought not to be doing anyway.
I slightly disagree here -- e.g., I like to have some mid-lower level units for certain topics (e.g., string utils, file utils, ...). If a higher unit that needs something from them in its interface (e.g., a type from the lower units used in a parameter of its own routines) would re-export everthying from it, this sounds to me like you ask your friend for a book, and he brings his toolbox because he needed a screwdriver to open a box in which he'd kept the book. ;-)
Put in another way: the current unit's interface looks after the "required definitions" on behalf of any users of the current unit by exporting those that are needed.
But IIUYC, it re-exports everything, not only the required definitions, doesn't it?
(To do this with more control - a good thing I think - you really need the ability to import only subsets of an interface.)
I certainly agree here. But as I said, this ability exists already.
Because of the way things work, there will almost always be a uses in both the interface and the implementation, unlike in BP units where you can use only only one uses in the interface section and rely on the implementation section picking this up. Having the uses in the interface imported by the implementation imposes restrictions on the interface and if you left things that way the idea of re-exporting interfaces of imported units would awkward.
I don't think so. BP does allow `uses' in the implementation part (in case that wasn't clear), so I don't think in the BP model you need to use any more units in the interface part than in the Apple model. (Though you *can* move the imports required by the implementation there as well in the BP model, but you don't have to.)
Not recommended in general, but on rare occasion useful - you can allow users to optionally relax the type checking across the interface/implementation barrier (comparing the implementation's definitions to declaration part of the interface) to allow explicit recasting of types for the rare occasions this is useful.
Strong objection here! :-)
Explicit type casting within an routine declaration is possible, in BP style (see the other thread). That's not too nice, but at least it's explicit, and someone who uses it can be supposed to know what they're doing. And they see the type cast right there where the code is that uses it.
"Casting" a routine type (i.e., the parameter form) is quite a different beast. IMHO that's exactly not an explicit, but rather a quite implicit cast (the types change magically on their way into the routine). I've commented on this WRT `univ' in procedural types.
(Now something like this is possible in GPC with the (ab)use of `external' and linker names, but it's certainly not recommended.)
And "relaxing the type checking" even sounds like doing it globally (not only for routines that are specially marked for "uncertain argument types"). This would be another order of magnitude worse since it would affect all routines in the interface. But maybe I'm misunderstanding you here.
C. Circular references.
Skating out on thin ice here...
If a unit's interface has already been imported, a re-importing of it is redundant (unless you allow importing only parts of an interface, but let's keep this simple for now). This ought to be able to break the deadlock -- ?
Which deadlock? There is no deadlock in either BP or EP circular references.
Frank