From: Robert A Duff on
Adam Beneschan <adam(a)irvine.com> writes:

> On Feb 9, 6:56�am, Robert A Duff <bobd...(a)shell01.TheWorld.com> wrote:
>> AdaMagica <christoph.gr...(a)eurocopter.com> writes:
>> > OK, but then you have a similar problem to Ada83's syntactically
>> > unneeded bodies which Ada95 solved with a pragma.
>>
>> I think that problem is an illusion. �There was a problem,
>> but it was a problem with implementations, not with the
>> language. �How do we know if a given package spec has
>> a body? �Simple: look on the disk and see if there's
>> a source file containing that body. �In GNAT, that would
>> mean looking for foo.adb.
>
> And for other implementations (that put fewer restrictions on the
> names and locations of source files containing the Ada source), that
> would mean searching every file on the disk to see if one of them had
> "package body foo" in it. :) :)

Sure. I don't see a problem with that. (It's not really "every file on
the disk" of course -- it's "every file with so-and-so extension in the
directories where the compiler was told to look".) AdaMagic, for
example, does this. The trick to making it efficient is to cache
the information in a UNIT.MAP file.

> I know---those other implementations wouldn't do things this way;
> they'd provide some other mechanism to allow a programmer to tell the
> compilation system that a package no longer requires a body---or does
> require a body. But you should be careful when you say things like
> "Simple...". Unless, of course, you were joking.

Not joking.

>...Anyway, the problem
> was certainly solvable in any implementation, although as Randy points
> out it's still not ideal because it makes it too easy for an
> accidental error to result in the compiler accepting a package without
> a body that's supposed to have one, or vice versa, causing incorrect
> results at runtime that could be puzzling to track down.

I suppose so, but I think that problem could be solved by a good
build system, too. It could complain about stray bodies lying
around the place with (misspelled) names that don't match any
spec.

>...And that
> issue exists with GNAT also---you delete some files from your
> directory with a wildcard, somehow foo.adb accidentally gets deleted
> along with them, and the compiler still thinks your program is OK.

Seems unlikely.

Here's a similar issue: One reasonable design pattern is to have
a body that contains a type extension, which registers itself
into some global data structure. All calls to this thing
are indirect (via the parent's 'Class). Nobody needs visibility
on this thing, so its spec is completely empty, except for
the obligatory pragma Elaborate_Body. Unfortunately, if you
forget to 'with' it, it will be silently ignored.

- Bob
From: Randy Brukardt on
"Robert A Duff" <bobduff(a)shell01.TheWorld.com> wrote in message
news:wccr5orgwhd.fsf(a)shell01.TheWorld.com...
> "Randy Brukardt" <randy(a)rrsoftware.com> writes:
>
>>...(For Janus/Ada, at least, every source file is compiled
>> separately, and code is generated as necessary without needing anything
>> other than direct semantic dependencies to have been previously compiled.
>> That model is impossible for separate private parts; the specification
>> would
>> not contain enough information to generate any code or any code for calls
>> to
>> it.)
>
> The Ada 83 model seemed to be that the compiler doesn't need to look at
> with'ed bodies to generate code. But that's bogus.

It's not bogus, the existence of Janus/Ada demonstrates that. And other Ada
83 compilers worked that way as well.

> All Ada compilers
> other than Janus/Ada look at bodies to generate code for generic
> instantiations. And you need to look at bodies to implement
> inlining properly. (Does Janus/Ada implement inlining?)
> Similarly for any other inter-package optimizations.

I don't believe in inlining(*). Nor inter-package optimizations in typical
compilation modes. Our model is that the only dependencies are the actual
semantic dependencies caused by with clauses (and stubs, which work much
like with clauses). Everything was designed to have tolerable compilation
speeds on original PCs, and no program library at all (just a pile of files
somewhere). Obviously, those choices aren't as important these days, I
wouldn't design it the same if I was starting today.

(*) I think inlining is something that should be done automatically by the
compiler. I see no reason at all to clutter source code with hints that the
compiler could figure out better on its own. It would be much better if the
source was annotated with the intended time/space tradeoffs: make this loop
as fast as possible, make this rarely used subsystem as small as possible,
make everything else "normal". This is one advantage that just-in-time
compilation has: it actually has data to suggest what is important.

I also believe that the only reasonable compilation model in the limit is of
full program compilation - if I started a compiler design today I would
build it around that model where most of the work is done at what today is
called "link-time". (Or even just-in-time.) So if we're talking hypothetical
languages, compilation would also be very different. But this is an Ada
forum, and I'm not much interested in hypotheticals.

Randy.


From: Randy Brukardt on
"Robert A Duff" <bobduff(a)shell01.TheWorld.com> wrote in message
news:wccvde3gwts.fsf(a)shell01.TheWorld.com...
> "Randy Brukardt" <randy(a)rrsoftware.com> writes:
....
>> Moreover, it is easy to imagine errors that would cause the unit to no
>> longer be the body (such as misspelling the name, or misspelling "body"),
>> at
>
> I see your point about misspelling the name. But misspelling "body"?
> That would just be a syntax error.

I often leave out "body" altogether. But either are a syntax error, but only
if the build system tries to compile the file! And why is it doing that,
build systems don't compile random files that aren't needed.

I could imagine a warning if you have strong links (naming conventions,
whatever) to this particular file, but that is often not the case. (It
wasn't in Janus/Ada until we added the project manager in the mid-90's - if
the file didn't have the right name or the right contents, it was ignored.
We didn't want to be picking up random documents and leftover debugger
files, which used to happen in the old days.)

>> which point the implementation would have to guess. But the problem of a
>> body that the programmer expected to be included being left out would
>> continue.
>
> But the so-called "solution" in Ada 95 didn't actually solve anything.
> Early versions of GNAT had the same bug -- they ignored a non-required
> body, simply by saying it's not part of the program library.
> That was fixed because it's bad behavior, not because it failed
> to conform to the Ada 95 rules.

I don't see how you could "fix" it in general. If the file matches the
naming conventions or already has a link from a project manager, sure you
can see it. But if those things aren't true, are you going to nag the user
everytime? (No body found for package X!) We actually did that for a while
in late Ada 83 days and it drove everybody nuts.

....
>> I'm also dubious of the basic idea. I suppose you could keep the private
>> part in a separate file, but I can't imagine any useful way to *compile*
>> it
>> separately (you'd have to have both parts available in order to do any
>> sort
>> of code generation).
>
> I don't think you need to look at the private part to generate code.
> If you change it to "to generate efficient code", then I'll agree.

I don't know how to compile a subprogram call if I don't know the parameter
passing kind (by-copy or by-reference). You could make everything
by-reference, but then you have to fake by-copy at the call site. Not sure
how you could do that if you don't know anything about the type.

It's the reason you can't make calls to subprograms with parameters of
incomplete types. (We recently discovered that Ada has been wrong about
disallowing the *declaration* of such subprograms; the only problem is
*calling* them; Ada 2012 will relax that restriction, which will be great
for "limited with".)

>>...So there wouldn't be much, if any, advantage in terms
>> of development.
>
> I disagree -- I think there are big advantages to storing the
> private part in a separate file. I don't much care what files
> the compiler wants to look at, so long as it's not too slow.

Maybe. It sounds like more separate windows to juggle in the editor.

> The private part is part of the implementation. It's useful
> to manage it separately in your CM system (and CM systems
> are file based). Consider, for example, a visible part
> that has multiple implementations (maybe target dependent),
> selected by the build scripts. It's really annoying to
> have to duplicate the visible part.

CM systems have been a pet peeve of mind for years. File-based management
simply doesn't work; unfortunately, everything else is too complex to use.
Janus/Ada plenty of files that are mostly shared (like the definition of
Standard for different targets), and it is pain to keep them all consistent.
I built a front-end to our CM system to track those relationships, but it
doesn't work that well because of the need to manually update things, even
when the change is in the shared part (which is the usual case).

There is no particular part of the code that is more or less common for
these "unshared" parts to occur. Bodies, private declarations, and visible
constants all are involved. So you're fixing one corner of a wide tools
problem with a language feature.

If you really wanted to fix that (without creating a new kind of CM
system!), I think you would need to support putting arbitrary parts of
compilation units in different files. Almost like the old include files, but
with first class support in the programming environment so it is easy to see
and work on the assembled result. But that isn't a language enhancement
per-se.

>>... (For Janus/Ada, at least, every source file is compiled
>> separately, and code is generated as necessary without needing anything
>> other than direct semantic dependencies to have been previously compiled.
>> That model is impossible for separate private parts; the specification
>> would
>> not contain enough information to generate any code or any code for calls
>> to
>> it.)
>
> This seems backwards. Of course you chose a compilation model
> based on the rules of Ada (Ada 83, in fact). But I'm speculating
> about what Ada should have been -- a language very similar
> to Ada, but slightly different. You can't argue against such
> a language by saying existing Ada compilers can't handle it.
> If Ada had been designed differently, then so would the compilers
> have been.

We're talking about Ada here (and possibly enhancements to Ada). I'm not
talking about the Duff language, nor am I very interested in it. Ada is
going to be compiled by Ada compilers, and the vast majority of them already
exist. How they work *is* relevant.

Randy.


From: Stephen Leake on
Robert A Duff <bobduff(a)shell01.TheWorld.com> writes:

> Adam Beneschan <adam(a)irvine.com> writes:
>
>>...And that
>> issue exists with GNAT also---you delete some files from your
>> directory with a wildcard, somehow foo.adb accidentally gets deleted
>> along with them, and the compiler still thinks your program is OK.
>
> Seems unlikely.

Which means it will be very hard/confusing to debug when it does
happen, since you will not be familiar with the problem. So it is
important for the tool to handle the unlikely situations nicely.

> Here's a similar issue: One reasonable design pattern is to have
> a body that contains a type extension, which registers itself
> into some global data structure. All calls to this thing
> are indirect (via the parent's 'Class). Nobody needs visibility
> on this thing, so its spec is completely empty, except for
> the obligatory pragma Elaborate_Body. Unfortunately, if you
> forget to 'with' it, it will be silently ignored.

And if you do 'with' it, GNAT will complain that it's unnecessary, so
you have to put a pragma Warnings (off) on it. QtAda does this for
some stuff; it's annoying.

I prefer to handle that case by making the registration function
visible in the package spec, rather than calling it in the package
body execution part. Then the main program has to call it, and no
compiler warnings are generated.

--
-- Stephe
From: Robert A Duff on
Stephen Leake <stephen_leake(a)stephe-leake.org> writes:

> Robert A Duff <bobduff(a)shell01.TheWorld.com> writes:
>
>> Adam Beneschan <adam(a)irvine.com> writes:
>>
>>>...And that
>>> issue exists with GNAT also---you delete some files from your
>>> directory with a wildcard, somehow foo.adb accidentally gets deleted
>>> along with them, and the compiler still thinks your program is OK.
>>
>> Seems unlikely.
>
> Which means it will be very hard/confusing to debug when it does
> happen, since you will not be familiar with the problem. So it is
> important for the tool to handle the unlikely situations nicely.

I suppose...

>> Here's a similar issue: One reasonable design pattern is to have
>> a body that contains a type extension, which registers itself
>> into some global data structure. All calls to this thing
>> are indirect (via the parent's 'Class). Nobody needs visibility
>> on this thing, so its spec is completely empty, except for
>> the obligatory pragma Elaborate_Body. Unfortunately, if you
>> forget to 'with' it, it will be silently ignored.
>
> And if you do 'with' it, GNAT will complain that it's unnecessary, so
> you have to put a pragma Warnings (off) on it. QtAda does this for
> some stuff; it's annoying.

But that's a hugely useful warning. Without it, you end up with
useless 'with's accumulating in your code. An occassional
pragma Warnings is well worth the trouble, IMHO.

> I prefer to handle that case by making the registration function
> visible in the package spec, rather than calling it in the package
> body execution part. Then the main program has to call it, and no
> compiler warnings are generated.

Self-initializing packages are a Good Thing, I think.

I often do it like this: The parent type is limited controlled,
and Initialize/Finalize install/remove the object into some data
structure. The derived type just overrides some primitive
ops (not Initialize/Finalize), and declares one or more
objects -- all in some package body.

- Bob