[fpc-pascal] child units (was: dot within unit file name)
Vinzent Hoefler
JeLlyFish.software at gmx.net
Wed Jan 23 08:57:54 CET 2008
On Tuesday 22 January 2008 21:36, Marco van de Voort wrote:
> > > type tmyopaguetype = type pointer;
> > >
> > > Since the interface must 100% define the interface so that it can
> > > be used, which is a hard rule with Pascal and Modula2.
> >
> > Hmm, what if
> >
> > --- 8< ---
> >
> > interface
> >
> > type
> > tMyOpaqueType; // Incomplete.
> >
> > private
> >
> > type
> > // Complete type declaration, but privately.
> > tMyOpaqueType = record ... end;
> >
> > --- 8< ---
> >
> > would be possible?
> >
> > The type is still declared in the interface part; yet, because it's
> > in the private part of the interface, no other module can use it's
> > inner structure.
>
> No. Completely defines (most notably its size, probably also its
> alignment if it applies). In practice, nearly all M2 compilers only
> support pointers
Yes, but that's what it does, defining it, I mean. The "private" keyword
would only mark it as "can't use, if you're not a child unit". It is
still in the interface, and thus "completely defined" AFAICS.
> > > This makes e.g. the operator overloading already hard, since it
> > > can't be a record (even variant).
> >
> > Well, yes and no. As I wrote, you'd need to store that information
> > in the unit file.
>
> You can't. Sb might already be needing the interface before the
> implementation is compiled.
Yes, I know that. (Ada is a lot more strict about it, where interface
(spec) and implementation (body) are even in different files and you
can actually compile your code without the body available.)
You may have overlooked it, but the "incomplete type" is already
completed while still in the interface part. To be more clear on the
subject, let me try a more complete example again:
--- 8< ---
unit
Foo;
interface
type
Bar = record; // Incomplete for now.
procedure FooBar (Arg : Bar);
private
type
Bar =
record
X : Integer;
Y : Double;
end;
implementation
...
end;
--- 8< ---
So it's still in the interface, the compiler has all the information it
needs (even if only the interface is available at time of compilation),
only that the inner working of the type is marked as sort
of "non-public".
It's more a question of the type system, rather than trickery with the
symbol tables.
Or you may view it as a special form of a forward declaration.
> > > It is a bit the .NET class helper (partial classes) trick in a
> > > modular setting.
> >
> > From what I understood, those partial classes are different. They
> > don't extend functionality, they split it (or maybe, the call it
> > "delegation of responsibility").
>
> As far as I see it they add functionality to a base container. In
> partial classes case a class, and in Ada's case a module.
Maybe. I'm not a .NET-expert, but then the name is confusing at least. I
understood that a class could be implemented in several different
modules (I understood "functionality sets") and only upon completion of
all modules/functionality, the resulting class as a whole is usable.
If that understanding is wrong, then I fail to see what "partial
classes" do differently than a normal class. In the end, classes are
there for extending them, that's more or less the whole point of OOP.
> Both also seem to require to include the relevant "helper" module
> explicitely to unlock the extension.
Well, in Ada there are no "helper" modules, so this must be wrong for at
least this case. ;)
> > > I don't like that either. The gain you get from your
> > > old and trusted code, you loose in complexity and clarity of
> > > overview in the module system
> >
[...]
> >
> > I mean, I'm quite sure, you're not argueing for an "all source in
> > one large file" approach... so instead of (shared) include files,
> > child package would provide compiler checked extensibility.
>
> I don't like the idea of modules being mutable (even if it only means
> extension of the container)
The modules do *not* change in any way. You can extend them only by
declaring it that way and need to put them in the "uses" clause if you
want to use that "extended functionality".
It's no more "mutable" than adding "Math_Complex" as another unit to
your source file as "extension" to the "Math" unit.
Actually, it only takes the modularization to another level and makes it
even more flexible.
> > IMO, it's much like the package system for the compiler. At least
> > how it's laid out: You have FCL that consists of FCL.Base, FCL.XML,
> > ...
>
> I don't see any relation.
Well, I do. YMMV.
> FCL only exists as parts of names and in
> documentation. On src level there are only the packages, and they are
> not spelled above (where they imply some hierarchic system), but with
> dashes that aren't separators.
Yes, I tried to make an example, how such a system would look at source
level. For me, I 1:1 relation between the documentation of an intended
hierarchy and its appropriate source would be a good thing.
> > > (an import of complexnumbers in one place might
> > > suddenly react differently due to the partial module/classhelper)
> >
> > But that's the case already. Even the order of imports is
> > semantically relevant (or well, it might be
>
> But does exuse throwing out all restraint?
On the contrary, I'm trying to throw it in by making it possible to make
some things more private than they are now.
> > Or what's with that "reuse" keywords for the uses clause?
>
> I'm no fan of that.
Glad to hear.
> But at least it doesn't require scanning and
> indexing all files up front. (though, admitted, neither do child
> packages, I just fail to see their use)
Well, I used child packages to a great extend, so I am bound to see
their use. And I missed them, because a hierarchical source layout for
certain modules is sometimes very desirable, even if only in respect to
possible name clashes.
> They do have in common that a use case that would interest me could
> swing me. And reuse seems to make some sense in breaking up large
> header sets, though I'm not really convinced yet.
It saves about 5 minutes of typing work in a typical project. But this
gets lost in the noise once you're trying to find out, which import
fails your compile. While I'm at it, I ran into an interesting issue
yesterday:
-- 8< -- random.pas --
unit
Random;
interface
implementation
end {Random}.
-- 8< --
-- 8< -- bp.pas --
unit
BP;
uses
Random;
interface
implementation
end {BP};
-- 8< --
-- 8< -- main.pas
uses
BP;
begin
WriteLn (Random (1.0));
end {Main}.
-- 8< --
|Compiling main.pas
|main.pas(5,13) Error: Identifier not found "Random"
|main.pas(5,20) Error: Illegal expression
Is this a bug in the compiler (I used fpc2.0.0)? Or is there any reason,
why this should happen? The unit "Random" has not been used by the main
program, so I would expect it to only find the "System.Random"
identifier here, not the identifier of a unit, I haven't even put in
the "uses" clause.
If I move the "uses Random" from the interface into the implementation
part of unit "BP", it compiles again, so it seems to be a problem with
the interface/implementation visibility.
> (I assumed a child package would be autoloaded into a module that
> imported the base package. Apparantly I'm wrong there)
Yes, you are. Of course this is not allowed to happen. Only explicitely
declared "uses" are allowed.
Vinzent.
More information about the fpc-pascal
mailing list