[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