comp.lang.ada
 help / color / mirror / Atom feed
* Re: To Initialise or not
  1996-04-29  0:00 To Initialise or not Steve O'Neill
@ 1996-04-29  0:00 ` Ken Garlington
  1996-04-29  0:00   ` Robert Dewar
  1996-04-30  0:00   ` Robert A Duff
  0 siblings, 2 replies; 52+ messages in thread
From: Ken Garlington @ 1996-04-29  0:00 UTC (permalink / raw)



Steve O'Neill wrote:
> 
> > As an aside, I always write ":= null;" when I want to *rely* on the
> > initial value of a pointer, even though I know that pointers are always
> > default-initialized to null.
> 
> You're not the only one. :)

I think I'm the only person in the world that _doesn't_ like this coding
style. I don't like it because I can't find a use for it. Generally, I only want
to enter information if there's some use for it, either to make the
program work, or to make it more readable, etc. Otherwise, I'm just adding another
source for error (suppose, for example, there's an object called mull, nell,
etc. of a compatible type and scope, and I make a typo?)

Some people say that initializing to null adds a hint to the reader that
it's an access. That seems (a) kind of redundant, since I should always use the
type of the object for such information, and (b) kind of dangerous, since
initial values for access types aren't always "null". If I use the keyword
to look for access values, I'll miss some.




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-29  0:00 ` Ken Garlington
@ 1996-04-29  0:00   ` Robert Dewar
  1996-04-30  0:00     ` Ken Garlington
  1996-04-30  0:00   ` Robert A Duff
  1 sibling, 1 reply; 52+ messages in thread
From: Robert Dewar @ 1996-04-29  0:00 UTC (permalink / raw)



"Some people say that initializing to null adds a hint to the reader that
it's an access. That seems (a) kind of redundant, since I should always use the
type of the object for such information, and (b) kind of dangerous, since
initial values for access types aren't always "null". If I use the keyword
to look for access values, I'll miss some."

I don't think that's the point at all, the point is to make it explicit
to the reader that the initial value of null is important from a semantic
point of view, rather than just an accident of the definition.





^ permalink raw reply	[flat|nested] 52+ messages in thread

* To Initialise or not
@ 1996-04-29  0:00 Steve O'Neill
  1996-04-29  0:00 ` Ken Garlington
  0 siblings, 1 reply; 52+ messages in thread
From: Steve O'Neill @ 1996-04-29  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> In article <318109B1.3B54AFBF@escmail.orl.mmc.com>,
> Theodore E. Dennison <dennison@escmail.orl.mmc.com> wrote:
> >I suspect it is silent on this issue, because the Ada (83) LRM is
> >NOT silent on the issue. Section 3.2.1(18) reads:
> >"The execution of a program is erroneous if it attempts to evaluate a
> > scalar variable with an undefined value."
>
> My personal coding convention is to *not* initialize variables if I know
> they will be properly initialized later.  <snip>

Another situation where initialization is ill-advised is when dealing with 
memory-mapped hardware registers.  It could be disastrous to write initial
values to such a register which is 'pre-initialized' by the hardware.  This 
is obviously a special case, but one that should not be ignored when dictating
that all variables be initialized by the software (either by developers or
automatically).

> As an aside, I always write ":= null;" when I want to *rely* on the
> initial value of a pointer, even though I know that pointers are always
> default-initialized to null.

You're not the only one. :)

-- 
Steve O'Neill                      | "No,no,no, don't tug on that!
Sanders, A Lockheed Martin Company |  You never know what it might
smoneill@sanders.lockheed.com      |  be attached to." 
(603) 885-8774  fax: (603) 885-4071|    Buckaroo Banzai




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-30  0:00     ` Ken Garlington
@ 1996-04-30  0:00       ` Robert A Duff
  1996-05-01  0:00         ` Keith Thompson
  1996-05-06  0:00         ` Ken Garlington
  0 siblings, 2 replies; 52+ messages in thread
From: Robert A Duff @ 1996-04-30  0:00 UTC (permalink / raw)



In article <3185E379.7C20@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>OK, so if an initialization to null lets me know that I may see the
>object compared to null later:

No, sorry, I just meant the "if ... = null" as an *example*.  The
general point is: If I explicitly initialize, then I plan to read that
value, at least in some cases.  If I don't, then I promise I won't read
that value.

>3. How do you relay this convention to the reader, so that they understand
>   what it means?

The coding convention is easy to understand.  Do you know when it's
necessary to explicitly initialize an Integer variable?  The coding
convention is to explicitly initialize pointer variables in exactly the
same cases -- namely, when the code plans to read that value, before
some other assingment statment.

In other words, pretend that you're coding in a language that's just
like Ada, except that there's no default initial value for pointers.
Write the code accordingly.  Since this mythical language is a subset of
Ada, this is safe.

>It just seems like effort without much payoff, at least from a maintainability
>standpoint...

Well, I admit, it's not that big a deal.  It's little effort, with
little payoff, so we could argue endlessly about the payoff.

>> ":= 0" might be mistaken for ":= O", for an integer, if there's
>> an integer variable O lying around.  Or ":= mull" might have meant ":=
>> nell", for a private type, if there are "mull"s and "nell"s lying
>> around.  This doesn't seem like a big risk to me.
>
>And I would equally object to someone initializing all integers to zero,
>just to show it's an integer, or to say that they _might_ be comparing the
>integer to zero later! 

I agree.  You don't initialize integers to show they're integers, and you
don't initialize them because of some specific comparison with zero.
You initialize them if and only if the initial value can be read.
I'm just advocating doing the same thing for pointers.

>...In my mind, if it's necessary to initialize
>the value, do so. If no initial value is needed (or the language gives
>you the correct initial value automatically), why take the risk, even if
>it's not a big one? Are you some sort of thrill-seeker? :)
>
>What about other default initializations, like others => ... ? It might
>not be less efficient (code-wise) to always spell out every component,
>and then you wouldn't be relying on the "efficiency hack" of this clause,
>and trusting the compiler to do the initialization of all unspecified
>components. Is this also part of such a coding style?

Other default initializations are different, since they are under the
control of the programmer, so you can define a "reasonable" default
initial value when appropriate, and you can write a comment allowing
clients to rely on it.  But initializing all access values to null does
*not* constitute a "reasonable" initial value, in many cases.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-30  0:00   ` Robert A Duff
  1996-04-30  0:00     ` Ken Garlington
@ 1996-04-30  0:00     ` Robert A Duff
  1996-05-01  0:00     ` Patrick Richard Wibbeler
  2 siblings, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-04-30  0:00 UTC (permalink / raw)



In article <DqnGoz.10v@world.std.com>,
I wrote:

>Ideally, the language would require detection of uninitialized
>variables, and an uninitialized pointer would be different from a null
>pointer.  The reason for not doing that are for efficiency, of course.

By the way, it always seemed silly to me that Ada 83 required
default-initialization of pointers to null, but integers are totally
undefined by default.  The idea was that dereferencing an uninitialized
pointer is *really* bad, whereas reading an uninitialized integer is
only mildly bad.  But that's not true.  "Ptr.all := 1;" and
"Array_Var(I) := 1;" are both equally bad in that they can both
overwrite arbitrary storage (if Ptr and I are not undefined).  Ada 95
fixes the array reference problem -- it can no longer overwrite
arbitrary storage.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-29  0:00 ` Ken Garlington
  1996-04-29  0:00   ` Robert Dewar
@ 1996-04-30  0:00   ` Robert A Duff
  1996-04-30  0:00     ` Ken Garlington
                       ` (2 more replies)
  1 sibling, 3 replies; 52+ messages in thread
From: Robert A Duff @ 1996-04-30  0:00 UTC (permalink / raw)



In article <3184E9CE.5C7A@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>Steve O'Neill wrote:
>> 
>> > As an aside, I always write ":= null;" when I want to *rely* on the
>> > initial value of a pointer, even though I know that pointers are always
>> > default-initialized to null.
>> 
>> You're not the only one. :)

>I think I'm the only person in the world that _doesn't_ like this
>coding style. I don't like it because I can't find a use for it.
>Generally, I only want to enter information if there's some use for it,
>either to make the program work, or to make it more readable, etc.

I think it makes the program more readable.  Well, it does if you
understand my coding convention.  ;-)

To me, "X: Some_Pointer;" means X is a pointer, which I plan to
initialize at some later point before reading it, whereas, "X:
Some_Pointer := null;" means X is is a pointer, where the first use
might well be "if X = null then ...".  Of course, I often write "X:
Some_Pointer := new T'(...);" or "X: Some_Pointer := Y;" or whatever
might be appropriate.  My point is just not to rely on the default value
of null.

Ideally, the language would require detection of uninitialized
variables, and an uninitialized pointer would be different from a null
pointer.  The reason for not doing that are for efficiency, of course.
I'm all for efficiency, but I don't like relying on this particular
efficiency hack in my source code, especially since putting ":= null"
shouldn't reduce efficiency.

>... Otherwise, I'm just adding another
>source for error (suppose, for example, there's an object called mull, nell,
>etc. of a compatible type and scope, and I make a typo?)

Shrug.  ":= 0" might be mistaken for ":= O", for an integer, if there's
an integer variable O lying around.  Or ":= mull" might have meant ":=
nell", for a private type, if there are "mull"s and "nell"s lying
around.  This doesn't seem like a big risk to me.

>Some people say that initializing to null adds a hint to the reader
>that it's an access. That seems (a) kind of redundant, since I should
>always use the type of the object for such information, and (b) kind of
>dangerous, since initial values for access types aren't always "null".
>If I use the keyword to look for access values, I'll miss some.

I agree -- maybe some people say it, but it's silly.  To me, it's not a
hint about what sort of type it is -- it's a hint that I intend to rely
on the initial value of null (as opposed to using null simply to detect
bogus pointer dereferences).

Unfortunately, it's like any idiom -- if you're not familiar with it, it
doesn't help.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-30  0:00   ` Robert A Duff
@ 1996-04-30  0:00     ` Ken Garlington
  1996-04-30  0:00       ` Robert A Duff
  1996-04-30  0:00     ` Robert A Duff
  1996-05-01  0:00     ` Patrick Richard Wibbeler
  2 siblings, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-04-30  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> To me, "X: Some_Pointer;" means X is a pointer, which I plan to
> initialize at some later point before reading it, whereas, "X:
> Some_Pointer := null;" means X is is a pointer, where the first use
> might well be "if X = null then ...".

OK, so if an initialization to null lets me know that I may see the
object compared to null later:

1. What do I do with this information? Why is it useful for me to know
   that you may later be comparing the value to null?

2. What should I conclude if this convention is broken -- for example,
   if you compare it to null, but don't explicitly initialize it to null,
   or conversely initialize it to null but fail to compare it to null?
   Should I be concerned? Is it just a case where a null compare was
   removed during maintenance, and I shouldn't care? Or what?

3. How do you relay this convention to the reader, so that they understand
   what it means?

It just seems like effort without much payoff, at least from a maintainability
standpoint...

> ":= 0" might be mistaken for ":= O", for an integer, if there's
> an integer variable O lying around.  Or ":= mull" might have meant ":=
> nell", for a private type, if there are "mull"s and "nell"s lying
> around.  This doesn't seem like a big risk to me.

And I would equally object to someone initializing all integers to zero,
just to show it's an integer, or to say that they _might_ be comparing the
integer to zero later! In my mind, if it's necessary to initialize
the value, do so. If no initial value is needed (or the language gives
you the correct initial value automatically), why take the risk, even if
it's not a big one? Are you some sort of thrill-seeker? :)

What about other default initializations, like others => ... ? It might
not be less efficient (code-wise) to always spell out every component,
and then you wouldn't be relying on the "efficiency hack" of this clause,
and trusting the compiler to do the initialization of all unspecified
components. Is this also part of such a coding style?

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-29  0:00   ` Robert Dewar
@ 1996-04-30  0:00     ` Ken Garlington
  0 siblings, 0 replies; 52+ messages in thread
From: Ken Garlington @ 1996-04-30  0:00 UTC (permalink / raw)



Robert Dewar wrote:
> 
> I don't think that's the point at all, the point is to make it explicit
> to the reader that the initial value of null is important from a semantic
> point of view, rather than just an accident of the definition.

However, the way the language is designed, shouldn't I always assume that
the initial value of null for an access value is significant to the
application, regardless as to whether it's implicit or explicit?

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00           ` Theodore E. Dennison
@ 1996-05-01  0:00             ` Robert A Duff
  1996-05-02  0:00               ` Theodore E. Dennison
                                 ` (2 more replies)
  1996-05-01  0:00             ` Dale Stanbrough
  1996-05-02  0:00             ` Robert Dewar
  2 siblings, 3 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-01  0:00 UTC (permalink / raw)



In article <318792E8.28CC1042@escmail.orl.mmc.com>,
Theodore E. Dennison <dennison@escmail.orl.mmc.com> wrote:
>Yes, but I don't think any compiler COULD easily help enforce this (It
>sounds a lot like the halting problem to me). That is what code 
>walkthroughs are for.

A compiler can *easily* detect uninitialized variables at run time.
Yes, it's nicer to know about it at compile time, when possible, but as
you say, it's not *always* possible.  You can (and should) use code
walkthroughs to detect divide-by-zero, too.  But Ada still detects that
problem at run time (in case the code walk through missed something).

The problem with detecting uninit vars at run time is of course
efficiency.  Plus the fact that it *might* make interfacing to other
languages and/or hardware more difficult.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00           ` Theodore E. Dennison
  1996-05-01  0:00             ` Robert A Duff
@ 1996-05-01  0:00             ` Dale Stanbrough
  1996-05-02  0:00             ` Robert Dewar
  2 siblings, 0 replies; 52+ messages in thread
From: Dale Stanbrough @ 1996-05-01  0:00 UTC (permalink / raw)



Keith Thompson writes:

"The only cost would be a simple check on access assignments, which
 probably could be optimized out in most cases."
 
 
...and the cost of modifying all that code that assumes that

	P1: Pointer_Type;
	
	
_is_ set to null (yes I know you were talking about running costs, but
still...)


Dale




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-30  0:00   ` Robert A Duff
  1996-04-30  0:00     ` Ken Garlington
  1996-04-30  0:00     ` Robert A Duff
@ 1996-05-01  0:00     ` Patrick Richard Wibbeler
  1996-05-06  0:00       ` Ken Garlington
  2 siblings, 1 reply; 52+ messages in thread
From: Patrick Richard Wibbeler @ 1996-05-01  0:00 UTC (permalink / raw)



One other point about the following exchange...
> >> > As an aside, I always write ":= null;" when I want to *rely* on the
> >> > initial value of a pointer, even though I know that pointers are always
> >> > default-initialized to null.
> >> 
> >> You're not the only one. :)
> 
> >I think I'm the only person in the world that _doesn't_ like this
> >coding style. I don't like it because I can't find a use for it.
> >Generally, I only want to enter information if there's some use for it,
> >either to make the program work, or to make it more readable, etc.

What if either of the following occur...
1] For some reason the language is changed so that pointers are no longer 
initialized to null as when C started using a negative 
number rather than zero as the end of file.  Those who had 
while(flag != EOF) rather than while(flag != 0) still had code that worked!

2]  Someone attempts to re-write the code in another language that 
doesn't initialize pointers to null.  He/she may not know whether or not 
to initialize the pointer to null without looking closely at the code..  
Readability is not just for now.  It is for later.

The main point is who knows who or why the code will be used in the 
future.  At such a small cost, the readability/maintainability in the 
future could be large.

Patrick

 




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-30  0:00       ` Robert A Duff
@ 1996-05-01  0:00         ` Keith Thompson
  1996-05-01  0:00           ` Robert A Duff
                             ` (2 more replies)
  1996-05-06  0:00         ` Ken Garlington
  1 sibling, 3 replies; 52+ messages in thread
From: Keith Thompson @ 1996-05-01  0:00 UTC (permalink / raw)



In <Dqp3HF.12t@world.std.com> bobduff@world.std.com (Robert A Duff) writes:
[...]
> No, sorry, I just meant the "if ... = null" as an *example*.  The
> general point is: If I explicitly initialize, then I plan to read that
> value, at least in some cases.  If I don't, then I promise I won't read
> that value.
[...]
> In other words, pretend that you're coding in a language that's just
> like Ada, except that there's no default initial value for pointers.
> Write the code accordingly.  Since this mythical language is a subset of
> Ada, this is safe.

I like the idea.  The only problem is that the compiler won't help
you enforce your promise not to read the value.  An explicit ":= null"
initialization is like a comment -- it's extremely useful, but it can
get out of sync with the actual code.

The real problem, I think, is that the access value null plays two
different roles: as an uninitialized (garbage) value, and as a legitimate
access value that doesn't designate any object.  If I were designing a
new language, I'd be tempted to define a second distinguished access
value called, say, "invalid".  The value null would act like it does
in Ada: it doesn't point to anything and an attempt to dereference it
raises Constraint_Error.  The value invalid would be the default initial
value of all access types; an attempt to dereference it *or to reference
it* would raise Constraint_Error.

For example:

    declare
        type Pointer_Type is access Some_Type;
        P1: Pointer_Type;       -- implicitly initialized to invalid
        P2: Pointer_Type := null;
        P3: Pointer_Type;
        Obj: Some_Type;
    begin
        Obj := P1.all;  -- raises Constraint_Error
        Obj := P2.all;  -- raises Constraint_Error
        P3 := P2;       -- ok, sets P3 to null
        P3 := P1;       -- raises Constraint_Error
    end;

The only cost would be a simple check on access assignments, which
probably could be optimized out in most cases.

Maybe for Ada 201Z?  8-)}

-- 
Keith Thompson (The_Other_Keith) kst@thomsoft.com <*>
TeleSoft^H^H^H^H^H^H^H^H Alsys^H^H^H^H^H Thomson Software Products
10251 Vista Sorrento Parkway, Suite 300, San Diego, CA, USA, 92121-2718
This sig uses the word "Exon" in violation of the Communications Decency Act.




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00         ` Keith Thompson
@ 1996-05-01  0:00           ` Robert A Duff
  1996-05-02  0:00             ` Keith Thompson
  1996-05-01  0:00           ` Theodore E. Dennison
  1996-05-06  0:00           ` Ken Garlington
  2 siblings, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-01  0:00 UTC (permalink / raw)



In article <Dqq1F9.CK3@thomsoft.com>, Keith Thompson <kst@thomsoft.com> wrote:
>The real problem, I think, is that the access value null plays two
>different roles: as an uninitialized (garbage) value, and as a legitimate
>access value that doesn't designate any object.

Exactly.

>...  If I were designing a
>new language, I'd be tempted to define a second distinguished access
>value called, say, "invalid".  The value null would act like it does
>in Ada: it doesn't point to anything and an attempt to dereference it
>raises Constraint_Error.  The value invalid would be the default initial
>value of all access types; an attempt to dereference it *or to reference
>it* would raise Constraint_Error.

Why restrict it to access values?  As I said in another post, an integer
array index is a pointer, just like an access value, and allowing
totally undefined array index values can do just as much damage as
allowing undefined access values.

While we're talking about language design: I think the idea of always
having a single "special" value, and always calling it "null" is ugly.
Some access types shouldn't *have* a null value at all.  (E.g. consider
a circular linked list, with a dummy header element.  The links can
*never* be null, unless there's a bug.)  And when I *do* need an extra
"special" value, why is it always called "null"?  In one case, it ought
to be called "End_Of_List".  In another case it should be called
"Not_Yet_Determined" or something.  And why can't I have *two* of the
special things?

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00         ` Keith Thompson
  1996-05-01  0:00           ` Robert A Duff
@ 1996-05-01  0:00           ` Theodore E. Dennison
  1996-05-01  0:00             ` Robert A Duff
                               ` (2 more replies)
  1996-05-06  0:00           ` Ken Garlington
  2 siblings, 3 replies; 52+ messages in thread
From: Theodore E. Dennison @ 1996-05-01  0:00 UTC (permalink / raw)



Keith Thompson wrote:
> 
> I like the idea.  The only problem is that the compiler won't help
> you enforce your promise not to read the value.  An explicit ":= null"
> initialization is like a comment -- it's extremely useful, but it can
> get out of sync with the actual code.

Yes, but I don't think any compiler COULD easily help enforce this (It
sounds a lot like the halting problem to me). That is what code 
walkthroughs are for.

-- 
T.E.D.          
                |  Work - mailto:dennison@escmail.orl.mmc.com  |
                |  Home - mailto:dennison@iag.net              |
                |  URL  - http://www.iag.net/~dennison         |




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00               ` Theodore E. Dennison
@ 1996-05-02  0:00                 ` Chris Warack <sys mgr>
  1996-05-02  0:00                   ` Robert A Duff
  1996-05-06  0:00                   ` Ken Garlington
  1996-05-02  0:00                 ` Robert A Duff
  1 sibling, 2 replies; 52+ messages in thread
From: Chris Warack <sys mgr> @ 1996-05-02  0:00 UTC (permalink / raw)



In article <3188AF51.1F1A7590@escmail.orl.mmc.com>, "Theodore E. Dennison" <dennison@escmail.orl.mmc.com> writes:
|> Robert A Duff wrote:
|> > 
|> > In article <318792E8.28CC1042@escmail.orl.mmc.com>,
|> > Theodore E. Dennison <dennison@escmail.orl.mmc.com> wrote:
|> > >Yes, but I don't think any compiler COULD easily help enforce this (It
|> > >sounds a lot like the halting problem to me). That is what code
|> > >walkthroughs are for.
|> > 
|> > A compiler can *easily* detect uninitialized variables at run time.
|> > Yes, it's nicer to know about it at compile time, when possible, but as
|> 
|> Well, no. A compiler can't detect a thing at run-time. The compiler is
|> not even running then. It is happily asleep on the disk, dreaming
|> compiler dreams.
|> 
|> I do see what you are getting at, but I don't think adding a "dirty
|> bit" to every varaible is a serious consideration. (At least, the
|> systems programmer in me HOPES it isn't).

Unfortunately, that's about the only option possible.  However, it's not
as gloomy as it might seem.  First, a clarification -- need a dirty bit
for each "object" not each variable (i.e., it has to handle "heap" data
as well as "stack" data).  Second, the bit doesn't have to be part of the
object (although adding such a flag as part of an ADT or class definition
is a simple way for the user to gain this feature).  You could think of 
this check as something like the elaboration check.

The advantage of having this in the language is that the compiler may be
able to optimise these checks away whereas if the user codes them, this
becomes difficult.  The reason is that the compiler could do the checks
as part of "calling" an operation and therefore determine from the calling
context whether the object was initialized or not.  The user code would
be in the implementation of the operation and could not benefit from the
calling context.

The extra time these checks would add is probably not worth it in many (most)
arenas.  But, if they existed, a pragma Supress(Initializion_Checks) could
turn them off.  I suppose that having them off by default may be best and
a pragma Initialization_Checks would turn them on.  Of course, this second
approach could be implementated right now as vendor-dependent pragma...

As some have stated, it could be simple to implement this for run-time check
of all variables (in fact, the compiler can catch a lot of these).  However,
I can think of two trickier cases...  1) tracking dynamically allocated objects,
and 2) completely handling component-wise initializations of arrays and records.
Other people have mentioned interfacing to other languages, but I'm not
sure that would be so difficult to handle (by nothing else than ignoring it,
perhaps)

Case 1 is tied into whatever book-keeping scheme you use.  With flags attached
to an object it is not hard, but other methods might be tricky.  Case 2 is not
a problem if you accept the initialization of any component as the initialization
of the object.  If you want complete initialization to be checked, this becomes
much more complicated -- and, of course, that's what you really want...

Could be an interesting master's project here...  Add initialization_checks
to GNAT such that it is transparent (I can turn initialization_checks off and
rep clauses and such are not affected -- only the exception behavior would
change).

-- 
Christopher A. Warack, Capt, USAF  cwarack@cs.usafa.af.mil (719) 472-2401
Computer Science Department, US Air Force Academy
This content in no way reflects the opinions, standards, or policy
of the USAF Academy, or the US government.




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00             ` Robert A Duff
  1996-05-02  0:00               ` Theodore E. Dennison
@ 1996-05-02  0:00               ` Michael F Brenner
  1996-05-02  0:00                 ` Robert A Duff
  1996-05-06  0:00               ` Ken Garlington
  2 siblings, 1 reply; 52+ messages in thread
From: Michael F Brenner @ 1996-05-02  0:00 UTC (permalink / raw)



I disagree that the compiler can "easily" detect uninitialized variables,
or detect them at all. It is not possible to write a compiler that will
detect the class of variables that are not initialized to a correct 
value in the variables range (that is, all uninitialized variables and
only unitialized variables), in a finite amount of time.
Mike Brenner <mikeb@mitre.org>




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00               ` Michael F Brenner
@ 1996-05-02  0:00                 ` Robert A Duff
  1996-05-04  0:00                   ` Kevin D. Heatwole
  0 siblings, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-02  0:00 UTC (permalink / raw)



In article <4masvn$1ai@linus.mitre.org>,
Michael F Brenner <mfb@mbunix.mitre.org> wrote:
>I disagree that the compiler can "easily" detect uninitialized variables,
>or detect them at all. It is not possible to write a compiler that will
>detect the class of variables that are not initialized to a correct 
>value in the variables range (that is, all uninitialized variables and
>only unitialized variables), in a finite amount of time.

I'm not sure who you're disagreeing with.  I don't think anybody
suggested that this was possible.  It's not, since it's undecidable, in
general (nothing to do with amounts of time!).

It *is* possible, however, to detect all uninitialized variables at run
time, and detect *some* of them at compile time.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00             ` Robert Dewar
  1996-05-02  0:00               ` Robert A Duff
@ 1996-05-02  0:00               ` Theodore E. Dennison
  1 sibling, 0 replies; 52+ messages in thread
From: Theodore E. Dennison @ 1996-05-02  0:00 UTC (permalink / raw)



Robert Dewar wrote:
> 
> T.E.D. said
> 
> "Yes, but I don't think any compiler COULD easily help enforce this  
> 
> (speaking of the idea of considering it to be "wrong" to reference a
> default initialized access value.
> 
> Help means help. It does not mean solving the halting problem. It is
> perfectly fine and easy for a compiler to give warnings in simple
> cases (it is no different from giving these warnings for the cases
> of other datatypes, such as integer). You can do it some of the time,
> and that is helpful.

Ahhh. If you are talking "in some cases", then you are quite right.
In fact, most compilers I have used DO give that kind of help. To
the best of my knowledge, there's not really any contreversy there.

My understanding was that we were discussing universal detection.

-- 
T.E.D.          
                |  Work - mailto:dennison@escmail.orl.mmc.com  |
                |  Home - mailto:dennison@iag.net              |
                |  URL  - http://www.iag.net/~dennison         |




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00                 ` Chris Warack <sys mgr>
@ 1996-05-02  0:00                   ` Robert A Duff
  1996-05-06  0:00                   ` Ken Garlington
  1 sibling, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-02  0:00 UTC (permalink / raw)



In article <4matk0$4k@usafa2.usafa.af.mil>,
Chris Warack <sys mgr> <sys mgr> wrote:
>Unfortunately, that's about the only option possible.  However, it's not
>as gloomy as it might seem.  First, a clarification -- need a dirty bit
>for each "object" not each variable (i.e., it has to handle "heap" data
>as well as "stack" data).

Well, that's not really a "clarification".  Heap data is variables, too.

In Ada, "object" means "variable or constant".  It has nothing to do
with whether the thing is stack-allocated or heap-allocated.  (Sorry for
being pedantic, after ranting about "Ada" vs. "ADA" pedants.  ;-) )

Presumably, any rule that prevents reads of uninitialized variables
would prevent uninitialized constants from existing at all (so no checks
would be required on reading a constant), because all constants are
initialized with some expression, and if *that* expression is a read of
an uninit var, it would raise an exception.

But a method that works for declared variables ought to work for heap
variables, too.  It seems silly to have a run-time method that works
only for stack variables, since that's the case that can *often* be
detected at compile time.

>...  Second, the bit doesn't have to be part of the
>object (although adding such a flag as part of an ADT or class definition
>is a simple way for the user to gain this feature).

As I said elsewhere, you don't usually need an entire extra bit for
this.  "type T is range 1..1_000_000;" fits in 32 bits.  If you add an
extra value for this sort of checking, it *still* fits in 32 bits.

Not making it part of the object is possible, but complicated, and
probably less efficient.

>...  You could think of 
>this check as something like the elaboration check.

I.e., grossly inefficient and disgusting?  ;-)

>The advantage of having this in the language is that the compiler may be
>able to optimise these checks away whereas if the user codes them, this
>becomes difficult.  The reason is that the compiler could do the checks
>as part of "calling" an operation and therefore determine from the calling
>context whether the object was initialized or not.  The user code would
>be in the implementation of the operation and could not benefit from the
>calling context.

I'm not sure what you're saying here.  Note that Ada defines a read of
an uninit *scalar* variable to be wrong, but not a read of an uninit
composite.  And that's good.  Consider a record containing an array of
integers, plus the index of the last-used element.  It is (and should
be) perfectly OK to pass that thing as a parameter, or assign "X:=Y",
even though the integers past the last-used one are uninitialized.

>The extra time these checks would add is probably not worth it in many (most)
>arenas.  But, if they existed, a pragma Supress(Initializion_Checks) could
>turn them off.  I suppose that having them off by default may be best and
>a pragma Initialization_Checks would turn them on.  Of course, this second
>approach could be implementated right now as vendor-dependent pragma...

The first could, too.  A vendor is allowed to define
implementation-defined check names that can be used in pragma Suppress.
So, a vendor could check (scalar) variables for uninitialized access,
and raise an exception in that case, and allow pragma Suppress to turn
it off.

(However, a vendor has to support packing -- e.g. a packed array of
Boolean has to take 1 bit per component.)

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00           ` Robert A Duff
@ 1996-05-02  0:00             ` Keith Thompson
  1996-05-03  0:00               ` Robert A Duff
  0 siblings, 1 reply; 52+ messages in thread
From: Keith Thompson @ 1996-05-02  0:00 UTC (permalink / raw)



In <Dqq7qB.LFn@world.std.com> bobduff@world.std.com (Robert A Duff) writes:
[...]
> Why restrict it to access values?  As I said in another post, an integer
> array index is a pointer, just like an access value, and allowing
> totally undefined array index values can do just as much damage as
> allowing undefined access values.

Because it's much more straightforward for access types (and for
floating-point types: NaN, Infinity, etc.) than for integer types.
It's easy to reserve as many special access values as you need.  Users --
and hardware -- tend to expect each possible bit-pattern of an integer
object to represent a valid integer value.  You could reserve the most
negative integer in a two's-complement representation, but that gets
complicated in the absence of hardware support.

> While we're talking about language design: I think the idea of always
> having a single "special" value, and always calling it "null" is ugly.
> Some access types shouldn't *have* a null value at all.  (E.g. consider
> a circular linked list, with a dummy header element.  The links can
> *never* be null, unless there's a bug.)  And when I *do* need an extra
> "special" value, why is it always called "null"?  In one case, it ought
> to be called "End_Of_List".  In another case it should be called
> "Not_Yet_Determined" or something.  And why can't I have *two* of the
> special things?

It isn't always called "null":

    End_Of_List: constant My_Access_Type := null;

If you want more than one special value, here's one way you might do it:

    End_Of_List           : constant My_Access_Type := null;
    Not_Yet_Determined    : constant My_Access_Type := new My_Object_Type;
    Insufficient_Caffeine : constant My_Access_Type := new My_Object_Type;

Of course, the compiler won't enforce your intention not to refer to
the designated objects.

If you happen to know how your compiler implements access types, you can
initialize the special constants with illegal pointer values, and hide
the details inside a private part or package body.  Hopefully attempts
to dereference these values will cause something bad to happen, ideally
an exception.

-- 
Keith Thompson (The_Other_Keith) kst@thomsoft.com <*>
TeleSoft^H^H^H^H^H^H^H^H Alsys^H^H^H^H^H Thomson Software Products
10251 Vista Sorrento Parkway, Suite 300, San Diego, CA, USA, 92121-2718
This sig uses the word "Exon" in violation of the Communications Decency Act.




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00           ` Theodore E. Dennison
  1996-05-01  0:00             ` Robert A Duff
  1996-05-01  0:00             ` Dale Stanbrough
@ 1996-05-02  0:00             ` Robert Dewar
  1996-05-02  0:00               ` Robert A Duff
  1996-05-02  0:00               ` Theodore E. Dennison
  2 siblings, 2 replies; 52+ messages in thread
From: Robert Dewar @ 1996-05-02  0:00 UTC (permalink / raw)



T.E.D. said

"Yes, but I don't think any compiler COULD easily help enforce this (It
sounds a lot like the halting problem to me). That is what code
walkthroughs are for."

(speaking of the idea of considering it to be "wrong" to reference a
default initialized access value.

Help means help. It does not mean solving the halting problem. It is
perfectly fine and easy for a compiler to give warnings in simple
cases (it is no different from giving these warnings for the cases
of other datatypes, such as integer). You can do it some of the time,
and that is helpful.

For example:

procedure q is
  type x is access integer;
  y : x;
  z : x;
begin
   z := y;
end;

This could certainly give a diagnostic if the appropriate option is set.

I almost wondered if it should give a diagnostic ("y is never assigned
a value") warning unconditionally. One version of GNAT did so, then we
took it out.

This is yet another case where I miss the capability of having a default
initialized constant. Yes in this case we could say

  y : constant x := null;

but in the record case, there is no easy way of doing this.





^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00             ` Robert Dewar
@ 1996-05-02  0:00               ` Robert A Duff
  1996-05-02  0:00               ` Theodore E. Dennison
  1 sibling, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-02  0:00 UTC (permalink / raw)



In article <dewar.831032134@schonberg>, Robert Dewar <dewar@cs.nyu.edu> wrote:
>Help means help. It does not mean solving the halting problem. It is
>perfectly fine and easy for a compiler to give warnings in simple
>cases (it is no different from giving these warnings for the cases
>of other datatypes, such as integer). You can do it some of the time,
>and that is helpful.

Yes, it is helpful, and it can be quite feasible to implement for local
variables.  But it's a lot harder for components of records on the heap,
accesses through complicated chains of pointers.  In most of the
programs *I* write (compilers and related tools), most of the data is
sitting in heap-allocated records, which is why I would like to have
run-time detection, in case the compile-time detection fails.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00             ` Robert A Duff
@ 1996-05-02  0:00               ` Theodore E. Dennison
  1996-05-02  0:00                 ` Chris Warack <sys mgr>
  1996-05-02  0:00                 ` Robert A Duff
  1996-05-02  0:00               ` Michael F Brenner
  1996-05-06  0:00               ` Ken Garlington
  2 siblings, 2 replies; 52+ messages in thread
From: Theodore E. Dennison @ 1996-05-02  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> In article <318792E8.28CC1042@escmail.orl.mmc.com>,
> Theodore E. Dennison <dennison@escmail.orl.mmc.com> wrote:
> >Yes, but I don't think any compiler COULD easily help enforce this (It
> >sounds a lot like the halting problem to me). That is what code
> >walkthroughs are for.
> 
> A compiler can *easily* detect uninitialized variables at run time.
> Yes, it's nicer to know about it at compile time, when possible, but as

Well, no. A compiler can't detect a thing at run-time. The compiler is
not even running then. It is happily asleep on the disk, dreaming
compiler dreams.

I do see what you are getting at, but I don't think adding a "dirty
bit" to every varaible is a serious consideration. (At least, the
systems programmer in me HOPES it isn't).

-- 
T.E.D.          
                |  Work - mailto:dennison@escmail.orl.mmc.com  |
                |  Home - mailto:dennison@iag.net              |
                |  URL  - http://www.iag.net/~dennison         |




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00               ` Theodore E. Dennison
  1996-05-02  0:00                 ` Chris Warack <sys mgr>
@ 1996-05-02  0:00                 ` Robert A Duff
  1 sibling, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-02  0:00 UTC (permalink / raw)



In article <3188AF51.1F1A7590@escmail.orl.mmc.com>,
Theodore E. Dennison <dennison@escmail.orl.mmc.com> wrote:
>Well, no. A compiler can't detect a thing at run-time. The compiler is
>not even running then. It is happily asleep on the disk, dreaming
>compiler dreams.

Yeah, yeah, yeah.  I meant that the code generated by the compiler can
do it.

>I do see what you are getting at, but I don't think adding a "dirty
>bit" to every varaible is a serious consideration. (At least, the
>systems programmer in me HOPES it isn't).

As I said, the argument against this is efficiency.  And, possibly, that
it might make interfacing to other languages or hardware more difficult.
(I can imagine ways to implement it invisibly.)

You don't have to add an entire bit, in most cases.  You need to add an
extra value, which will add an extra bit only if the type you're talking
about *exactly* uses up all the bit patterns.

Initializing all access types to "null" is pretty much the same
technique -- except it doesn't detect *reads* of null pointers, but just
*dereferences* of null pointers.  Does the "systems programmer in you"
find this ludicrously expensive?  It is, in fact, expensive, in some
cases.  For example, I recently profiled a program I wrote, and found
that it spent most of it's time initializing arrays-of-access to null
values!  On the other hand, it does detect some bugs.  For an integer
type that doesn't exactly match the hardware supported bounds (e.g. type
T is range 1..1_000_000), there's no need to add an extra bit.

I once used a Pascal compiler where type Integer was defined to be
-2**31..2**31, and the extra value -2**31-1 was used to detect
uninitialized variables.  This was quite useful.  If my program had been
too slow, I might have wanted a way to turn this feature off.  But it
wasn't.  Also, if my program had wanted to interface to the actual
hardware notion of a 32-bit integer, I might have wanted a way to turn
it off.

Run-time detection of uninitialized variables is useful, but has some
cost.  It seems to me that only the application programmer can
reasonably make this trade-off.  The cost is different, depending on
various properties of the application, and whether the cost is tolerable
also depends on the application.  (For a packed array of Booleans,
adding an extra "uninit" value *doubles* the size of the array!)

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00             ` Keith Thompson
@ 1996-05-03  0:00               ` Robert A Duff
  0 siblings, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-03  0:00 UTC (permalink / raw)



In article <Dqsx4y.FHK@thomsoft.com>, Keith Thompson <kst@thomsoft.com> wrote:
>Because it's much more straightforward for access types (and for
>floating-point types: NaN, Infinity, etc.) than for integer types.
>It's easy to reserve as many special access values as you need.  Users --
>and hardware -- tend to expect each possible bit-pattern of an integer
>object to represent a valid integer value.  

When I say "type T is range 1..1_000_000;", there are *plenty* of extra
bit patterns for the compiler to use.  You're correct in those cases
where the range of the integer type matches the hardware, of course.

>...You could reserve the most
>negative integer in a two's-complement representation, but that gets
>complicated in the absence of hardware support.

Complicated?  It gets inefficient, that's for sure.  And it's annoyance
if you need the value -2**31.  But it doesn't seem particularly
complicated for the compiler writer.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00                 ` Robert A Duff
@ 1996-05-04  0:00                   ` Kevin D. Heatwole
  0 siblings, 0 replies; 52+ messages in thread
From: Kevin D. Heatwole @ 1996-05-04  0:00 UTC (permalink / raw)



In article <DqsMC6.IwK@world.std.com>, bobduff@world.std.com (Robert A
Duff) wrote:
> It *is* possible, however, to detect all uninitialized variables at run
> time, and detect *some* of them at compile time.

I just thought I'd point out that OC System's PowerAda product includes
a tool to detect all uninitialized variables at runtime.  We call the
tool "afun" (Ada Find UNinitialized variables) which is based upon our
Aprobe debugging/monitoring technology (which is unique amongst all the 
compiler vendors).

Afun works at the machine level by "patching" all loads/stores to memory.
As the program executes, it keeps track of which locations have been
stored to and logs a call traceback whenever the program attempts to load
from an uninitialized location.  This technique can slow down a program
a bit, but it can be very valuable at ferreting out a use of an uninitialized
variable (in my experience, these bugs can be the hardest to weed out of
an application since the use of an uninitialized variable may not show up
during testing but wait until some inopportune time operationally).

Anyway, afun isn't perfect since it can identify locations that were 
initialized as being uninitialized (e.g., the memory location is shared
with another process and the other process initialized the location), and
it can take some expertize to use.  However, it is generally worth the
effort if you want to minimize the defects in your applications.

I just thought I'd point this out, since some problems (like uninitialized
variables) are best solved by good tools (even if the programming language
generally helps to avoid the problem).

Kevin Heatwole
OC Systems, Inc.




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-04-30  0:00       ` Robert A Duff
  1996-05-01  0:00         ` Keith Thompson
@ 1996-05-06  0:00         ` Ken Garlington
  1996-05-07  0:00           ` Robert A Duff
  1 sibling, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-05-06  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> No, sorry, I just meant the "if ... = null" as an *example*.  The
> general point is: If I explicitly initialize, then I plan to read that
> value, at least in some cases.  If I don't, then I promise I won't read
> that value.

I'm not trying to be initially obscure, but I'm really having trouble seeing
the power of this knowledge. If you "promise not to read a value," does that
make it write-only? Unused?

> 
> >3. How do you relay this convention to the reader, so that they understand
> >   what it means?
> 
> The coding convention is easy to understand.  Do you know when it's
> necessary to explicitly initialize an Integer variable?

Sure - when the code will fail to function correctly if I don't. However,
that's not the case here, since the code will function exactly the same
whether null is implicit or explicit.

> The coding
> convention is to explicitly initialize pointer variables in exactly the
> same cases -- namely, when the code plans to read that value, before
> some other assingment statment.

However, there's a difference. For example, consider this case with Integer:

declare
  Q : Integer;
begin
  Do_Something;

  if Q = 7 then
   ...
  end if;

end;

This code is erroneous. I know this because I don't have Q initialized prior
to the test. I can make it work by giving an initial value to
Q, either at declaration or just before the "if". On the other hand:

declare
  type A_Type is access ...
  A: A_Type;
begin

  Do_Something;

  if A = null then
    ...
  end if;

end;
   
This code is just fine. I don't look at this code, and say, "But wait a
minute! He didn't set A to null before he read A!" I know that A is null
at this point. It's a little odd to have a test that will always pass,
but setting A to null explicitly will not make the test more understandable
to me.

Moving on:

declare
  type A_Type is access ...
  A: A_Type;
  B: A_Type := new ...
begin

  Do_Something;

  if A = B then
    ...
  end if;

end;

This code is also odd, since the test will always fail. Will setting A
to null make it less odd?

However, I think you've missed my point. Let me retry: Do you have a block
of comments in each compilation unit that says:

-- NOTE: When a declaration using an access type has an explicit initial
-- value of null, this means that the declared object can be used without
-- any further initialization.

> In other words, pretend that you're coding in a language that's just
> like Ada, except that there's no default initial value for pointers.
> Write the code accordingly.  Since this mythical language is a subset of
> Ada, this is safe.

WHAT?

ATTENTION: SUBSETTING A LANGUAGE DOES NOT, IN ITSELF, MAKE IT SAFE.

If you believe implicit default values for access types are unsafe, then you
have to justify that statement. You also have to say why the alternative (adding
explicit code to replace the implicit code, under certain circumstances) is
comparatively safer.

> Well, I admit, it's not that big a deal.  It's little effort, with
> little payoff, so we could argue endlessly about the payoff.

We could. However, there is also the KISS principle, which in part says
"things are only added when they have sufficient justification." In other
words, if you want to add code, you have to show a clear reason why (particularly
for safety-critical code). You don't normally say, "It's fairly easy to
add, so I'll add it, and someone will have to justify taking it out."

> I agree.  You don't initialize integers to show they're integers, and you
> don't initialize them because of some specific comparison with zero.
> You initialize them if and only if the initial value can be read.
> I'm just advocating doing the same thing for pointers.

I understand that you want pointers to act like integers. I just don't
understand _why_, since pointers are conceptually different from integers.
Here's a different example: Suppose you saw code that looked like:

  X : Integer := Init (Thousands => 1, Hundreds => 4, others => 0)
                                             -- set X to 1400.

Would this be a preferred approach, since I'm just creating numeric literals
the way I create array aggregates?

> But initializing all access values to null does
> *not* constitute a "reasonable" initial value, in many cases.

This is certainly true. However, can you identify a case where null _is_ a
reasonable initial value, but not a reasonable initial _default_ value?

I'm not advocating the use of null as an initial value for all accesses, I'm just 
saying if an acceptable initial value is null, then it should be acceptable for 
the _default_ initial value to be null. At least when I read the RM, it doesn't
appear to make a clear distinction between null as a literal and null as a
default value.

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00         ` Keith Thompson
  1996-05-01  0:00           ` Robert A Duff
  1996-05-01  0:00           ` Theodore E. Dennison
@ 1996-05-06  0:00           ` Ken Garlington
  2 siblings, 0 replies; 52+ messages in thread
From: Ken Garlington @ 1996-05-06  0:00 UTC (permalink / raw)



Keith Thompson wrote:
> 
> I like the idea.  The only problem is that the compiler won't help
> you enforce your promise not to read the value.  An explicit ":= null"
> initialization is like a comment -- it's extremely useful, but it can
> get out of sync with the actual code.

By the way, my experience with maintaining code is that anything that is
likely to get out of sync with the actual code is actively harmful, and
has to have solid justification.

Also, a comment is only useful if it clearly communicates its intent to
the reader of the code, particularly if the reader is not the author.
I'm not sure this technique falls into that category without the reader
having more information (like a copy of your coding standards). And,
if the reader doesn't have access to the coding standards...

--- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00     ` Patrick Richard Wibbeler
@ 1996-05-06  0:00       ` Ken Garlington
  0 siblings, 0 replies; 52+ messages in thread
From: Ken Garlington @ 1996-05-06  0:00 UTC (permalink / raw)



Patrick Richard Wibbeler wrote:
> 
> What if either of the following occur...
> 1] For some reason the language is changed so that pointers are no longer
> initialized to null

Then I will personally find the instigators of this change, and SHOOT THEM.

At least using Ada 95 as a model for how Ada will evolve, I would say that
this is a risk that can reasonably be discounted. Furthermore, it is a risk
that can be applied equally well to every other language feature. For example,
what if the ability to use "others" in aggregates is removed?

I wouldn't use the evolution of C as a guide to the evolution of Ada.

> 2]  Someone attempts to re-write the code in another language that
> doesn't initialize pointers to null.  He/she may not know whether or not
> to initialize the pointer to null without looking closely at the code..

Not true. If someone is doing a mechanical translation of Ada to language X
(or X++?), then they should know that all pointers must have an initial value,
and that, unless explictly stated, the initial value is null. They should also
know that all attempts to dereference that object should be preceded by a check
to see if the object is null (otherwise, they aren't preserving the Ada semantics).

You could argue that the mechanical translation can be optimized, and that's true,
but you can do that optimization without the explicit initialization. In fact,
explicit initialization may make it harder to do the analysis, depending upon what
kind of flow analysis tools are being used.

You could also argue that the maintainer has a poor knowledge of Ada (or
X), and that explicit initializations make it less necessary to know the rules
of Ada, but that's going to get you into trouble that no amount of explicit
initialization will help!

Of course, it gets interesting if the target language has no concept of null
(or for that matter, of access types!). However, explicitly setting pointers
to null won't help that, either.

> Readability is not just for now.  It is for later.

Exactly. This is why anything added for readability should be _obvious_ to the
future maintainer, since the author isn't necessarily going to be around to
explain his coding style. I'm not seeing how this approach is quite so obvious
(particularly given the divergent explanations from its practitioners as to what
it's supposed to mean!).

> The main point is who knows who or why the code will be used in the
> future.  At such a small cost, the readability/maintainability in the
> future could be large.

Adding lots of little things at a small cost, without justifying them,
can often add up to something large in the future -- a large negative
maintenance cost.

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-01  0:00             ` Robert A Duff
  1996-05-02  0:00               ` Theodore E. Dennison
  1996-05-02  0:00               ` Michael F Brenner
@ 1996-05-06  0:00               ` Ken Garlington
  1996-05-07  0:00                 ` Robert A Duff
  2 siblings, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-05-06  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> A compiler can *easily* detect uninitialized variables at run time.

In Ada 95, you _can_ easily detect uninitialized variables using pragma
Normalize_Scalars and/or 'Valid, so long as an "invalid" representation of the 
scalar is possible.

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-02  0:00                 ` Chris Warack <sys mgr>
  1996-05-02  0:00                   ` Robert A Duff
@ 1996-05-06  0:00                   ` Ken Garlington
  1 sibling, 0 replies; 52+ messages in thread
From: Ken Garlington @ 1996-05-06  0:00 UTC (permalink / raw)



Chris Warack <sys mgr> wrote:
> 
> In article <3188AF51.1F1A7590@escmail.orl.mmc.com>, "Theodore E. Dennison" <dennison@escmail.orl.mmc.com> writes:
> |> I do see what you are getting at, but I don't think adding a "dirty
> |> bit" to every varaible is a serious consideration. (At least, the
> |> systems programmer in me HOPES it isn't).
> 
> Unfortunately, that's about the only option possible.

Well, Ada 95 has a solution for those cases where the type doesn't occupy all
values of the underlying hardware representation, so if you want this for
all variables, just make sure your types always have more bits allocated
than needed, and you're set!

(see: pragma Normalize_Scalars, 'Valid).

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-06  0:00               ` Ken Garlington
@ 1996-05-07  0:00                 ` Robert A Duff
  1996-05-08  0:00                   ` Ken Garlington
  0 siblings, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-07  0:00 UTC (permalink / raw)



In article <318E43A4.390E@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>In Ada 95, you _can_ easily detect uninitialized variables using pragma
>Normalize_Scalars and/or 'Valid, so long as an "invalid" representation of the 
>scalar is possible.

Well, sort of.  The use of an invalid representation will not
necessarily be detected in all cases.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-06  0:00         ` Ken Garlington
@ 1996-05-07  0:00           ` Robert A Duff
  1996-05-08  0:00             ` Ken Garlington
  0 siblings, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-07  0:00 UTC (permalink / raw)



In article <318E3A26.3565@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>I'm not trying to be initially obscure, but I'm really having trouble seeing
>the power of this knowledge. If you "promise not to read a value," does that
>make it write-only? Unused?

I'm don't understand why you don't understand.  I hesitate to prolong a
debate about a minor issue.  But it's more fun that *some*
discussions...

Let me ask you this:  Suppose Ada had a rule that all integers were
default-initialized to T'First, where T is the subtype of the variable.
Would you be happy with that language?  Would it change the way you
write code?

>However, I think you've missed my point. Let me retry: Do you have a block
>of comments in each compilation unit that says:
>
>-- NOTE: When a declaration using an access type has an explicit initial
>-- value of null, this means that the declared object can be used without
>-- any further initialization.

No, of course not.  It only needs to be stated once.  And there are lots
of other things that need to be stated once, and you need to give the
reader some way of easily finding that place.  I freely admit that if
somebody doesn't understand the coding convention, it won't help them
understand the code.  (It probably won't hurt, either, though.)

>> In other words, pretend that you're coding in a language that's just
>> like Ada, except that there's no default initial value for pointers.
>> Write the code accordingly.  Since this mythical language is a subset of
>> Ada, this is safe.
>
>WHAT?
>
>ATTENTION: SUBSETTING A LANGUAGE DOES NOT, IN ITSELF, MAKE IT SAFE.

ATTENTION: Please understand what I meant by "safe".  I meant, any
coding convention which "pretends" to code in a strict subset of Ada is
a "safe" coding convention, in the sense that the coding convention
cannot produce bugs.

In this example, I meant that it is safe to pretend that Ada doesn't
initialize pointers.  Please re-read what I wrote in this light -- that
is, when I said "THIS is safe", I meant "THIS PRETENSE is a safe
assumption".

This hypothetical not-quite-Ada language was an attempt to answer your
comment saying that it is hard to understand *what* the coding
convention *is*.  It's *not* hard, if you're capable of imagining such a
language.  That's all I meant.  It was not meant as a *justification* of
the coding convention itself (which is based on a minor readability
concern).

It doesn't make sense to answer this hypothetical explanation with,
"But, in Ada 95, pointers *are* initialized to null.  The RM says so."
I *know* that (I *wrote* the darn RM -- well, actually, I think Tucker
wrote that part :-) ), but I'm asking you to imagine an alternative.

By the way, all your examples concerned local variables.  The more
interesting cases are package-body variables, and record components.

>If you believe implicit default values for access types are unsafe, then you
>have to justify that statement. You also have to say why the alternative (adding
>explicit code to replace the implicit code, under certain circumstances) is
>comparatively safer.

No, no, no.  That's not what I meant about "safe".  Of course implicit
null's are just as safe as explicit nulls.

It just seems to me that it's valuable to know whether a given value is
going to be used.  It makes the code more readable, IMHO.  Null is not
special in that regard.

>> Well, I admit, it's not that big a deal.  It's little effort, with
>> little payoff, so we could argue endlessly about the payoff.
>
>We could. However, there is also the KISS principle, which in part says
>"things are only added when they have sufficient justification." In other
>words, if you want to add code, you have to show a clear reason why (particularly
>for safety-critical code). You don't normally say, "It's fairly easy to
>add, so I'll add it, and someone will have to justify taking it out."

In this case, the Ada *language* is what violates the KISS principle.  I
understand the reasons behind it, but still, the rule is that some
variables get a "junk" initial value, and some variables get a
well-defined initial value (by default, of course), which is obviously
more complicated than treating all variables the same.

I claim that the coding convention we're arguing about is actually
*more* KISS, since it treats integers and pointers alike.

>I understand that you want pointers to act like integers. I just don't
>understand _why_, since pointers are conceptually different from integers.

I don't see any way in which integers and pointers are different with
respect to *this* issue (whether or not they should be initialized).  I
understand that they *are* different with respect to this issue in Ada.
But that doesn't mean they are *conceptually* different.

>Here's a different example: Suppose you saw code that looked like:
>
>  X : Integer := Init (Thousands => 1, Hundreds => 4, others => 0)
>                                             -- set X to 1400.
>
>Would this be a preferred approach, since I'm just creating numeric literals
>the way I create array aggregates?

Sorry, but you lost me there.

>> But initializing all access values to null does
>> *not* constitute a "reasonable" initial value, in many cases.
>
>This is certainly true. However, can you identify a case where null _is_ a
>reasonable initial value, but not a reasonable initial _default_ value?

Sure.  I have a hard time imagining a type that is otherwise.

Suppose (stealing from another thread;-)) we have Patients and Doctors.
Each Patient is represented by a record, containing (among other
things), a pointer to its Doctor.  Because of the way this application
works, you can't create a Patient without giving it a Doctor -- i.e.
there is no reasonable default value for the Doctor field, and it must
always be non-null.

However, there's a hash table, implemented as an array of pointers to
Doctors.  I want to initialize all the slots to "null", which means
"this slot is empty now".  Conceptually, the array elements should be
initialized to null, whereas the My_Doctor field of Patient should be
initialized to "Don't touch this!".

I would write ":= (others => null);" on the array, to signify that I
intend to *depend* upon the initial null value, but I would *not* write
":= null" on the My_Doctor component of type Patient, to signify that I
intend to always explicitly initialize it.

>I'm not advocating the use of null as an initial value for all
>accesses, I'm just saying if an acceptable initial value is null, then
>it should be acceptable for the _default_ initial value to be null. At
>least when I read the RM, it doesn't appear to make a clear distinction
>between null as a literal and null as a default value.

Right, your coding convention is just as valid Ada as mine.

Sorry for the perhaps overly strident tone above.  I'm just frustrated
that I can't get across what I'm trying to say.  :-)

Here's a summary:

1. I think this coding convention make code just a little bit more
readable.  It makes a distinction between an initial value that I plan
to read, and an initial value that I don't.

2. I admit that it won't work if the reader doesn't know the convention.

3. The coding convention itself is easy to understand (i.e., it's easy
to understand when to put ":= null" or ":= something-else" on an access
variable or record component or whatever), if you imagine a language
just like Ada, except that it doesn't have the default-null rule.

4. I admit that it has the same problem as comments: if you don't do it
right, the compiler won't tell you so.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-07  0:00           ` Robert A Duff
@ 1996-05-08  0:00             ` Ken Garlington
  1996-05-09  0:00               ` Robert A Duff
  0 siblings, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-05-08  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> Let me ask you this:  Suppose Ada had a rule that all integers were
> default-initialized to T'First, where T is the subtype of the variable.
> Would you be happy with that language?  Would it change the way you
> write code?

I would be happy with that language (ignoring the resource utilization
effects), and any time I had an integer that I needed to initialize to
T'First, I would leave off the initialization (knowing that it was going
to be set to T'First anyway). Why not?

> >However, I think you've missed my point. Let me retry: Do you have a block
> >of comments in each compilation unit that says:
> >
> >-- NOTE: When a declaration using an access type has an explicit initial
> >-- value of null, this means that the declared object can be used without
> >-- any further initialization.
> 
> No, of course not.  It only needs to be stated once.

Where?

> And there are lots
> of other things that need to be stated once, and you need to give the
> reader some way of easily finding that place.

Could you give some examples?

> I freely admit that if
> somebody doesn't understand the coding convention, it won't help them
> understand the code.  (It probably won't hurt, either, though.)

I disagree. Any time I maintain code that does something non-obvious, I have to 
stop and figure out why the code was written in the strange manner. (Did
he really just set that pointer to null, or did I misread "null"?)

> In this example, I meant that it is safe to pretend that Ada doesn't
> initialize pointers.  Please re-read what I wrote in this light -- that
> is, when I said "THIS is safe", I meant "THIS PRETENSE is a safe
> assumption".

I disagree. Re-read my comments about valid access objects which can be
named similarly to null. In a more general sense, the more (unnecessary)
code written, the greater the likelihood of error.
> 
> This hypothetical not-quite-Ada language was an attempt to answer your
> comment saying that it is hard to understand *what* the coding
> convention *is*.  It's *not* hard, if you're capable of imagining such a
> language.

And you have the context of this discussion. Without your explanation,
within ready reach of the reader, it is quite difficult to guess the
magic intent of the explicit initialization to null. At least, it is for
me.

> It doesn't make sense to answer this hypothetical explanation with,
> "But, in Ada 95, pointers *are* initialized to null.  The RM says so."

But it does make sense to ask, "How does the reader get this explanation?"
It's in some "central" place, apparently, but that's all I know so far...

It also does make sense to ask, as I did originally, "Why is this convention
useful?" I'm still not sure I see that part of the discussion...

> By the way, all your examples concerned local variables.  The more
> interesting cases are package-body variables, and record components.

Then present an example  of either type, showing why this convention conveys
useful information.

> No, no, no.  That's not what I meant about "safe".  Of course implicit
> null's are just as safe as explicit nulls.

Safer, IMHO.

> It just seems to me that it's valuable to know whether a given value is
> going to be used.  It makes the code more readable, IMHO.

I guess I would be more convinced with an actual example.

Does this approach extend to other cases? For example, suppose a record
object is being assigned with an aggregate, and some of the aggregate values
will be used in the code, and others will be overwritten first. Do you have
some mechanism for distinguishing them?

What about loops? When I write "for J in Buffer'Range loop", there's
an implicit initialization of J to Buffer'First. Is there an issue here?

It just seems like a strange way to represent data flow information.

> In this case, the Ada *language* is what violates the KISS principle.  I
> understand the reasons behind it, but still, the rule is that some
> variables get a "junk" initial value, and some variables get a
> well-defined initial value (by default, of course), which is obviously
> more complicated than treating all variables the same.

However, if you want all (scalar) variables to get a well-defined default
initial value, you can do that in Ada 95 with Normalize_Scalars (and 'Size),
right? I don't know why this is critical for you, but it's there if you want
it...

> I claim that the coding convention we're arguing about is actually
> *more* KISS, since it treats integers and pointers alike.

I disagree that consistency necessarily equates to simplicity.
(See the example below).

> >I understand that you want pointers to act like integers. I just don't
> >understand _why_, since pointers are conceptually different from integers.
> 
> I don't see any way in which integers and pointers are different with
> respect to *this* issue (whether or not they should be initialized).

Well, assuming you don't resort to Unchecked_Conversion or something nasty
like that, any cases I can think of where you can use integers to reference
a memory location will automatically have bounds (e.g., as an array reference).
Pointers are unbounded in Ada 95, as far as I can tell -- there is no easy
way to check for "in range" in a machine-independent fashion. Therefore,
to avoid illegal references, you have to do something special, right?

> >Here's a different example: Suppose you saw code that looked like:
> >
> >  X : Integer := Init (Thousands => 1, Hundreds => 4, others => 0)
> >                                             -- set X to 1400.
> >
> >Would this be a preferred approach, since I'm just creating numeric literals
> >the way I create array aggregates?
> 
> Sorry, but you lost me there.

To slightly misquote a famous man: "I claim that the coding convention I'm 
proposing is actually *more* KISS, since it treats integers and arrays alike....
I don't see any way in which integers and arrays are different with
respect to *this* issue (how they should be initialized)."

However, applying my guideline that you don't write extra code unless there's
a clear purpose, this coding convention for integer initialization is silly.
It doesn't communicate, it obfuscates.

> 
> >> But initializing all access values to null does
> >> *not* constitute a "reasonable" initial value, in many cases.
> >
> >This is certainly true. However, can you identify a case where null _is_ a
> >reasonable initial value, but not a reasonable initial _default_ value?
> 
> Sure.  I have a hard time imagining a type that is otherwise.
> 
> Suppose (stealing from another thread;-)) we have Patients and Doctors.
> Each Patient is represented by a record, containing (among other
> things), a pointer to its Doctor.  Because of the way this application
> works, you can't create a Patient without giving it a Doctor -- i.e.
> there is no reasonable default value for the Doctor field, and it must
> always be non-null.

Wait a minute! In this case, null is neither a reasonable initial value, nor
a reasonable initial default value. Therefore, it is not a valid example.

> However, there's a hash table, implemented as an array of pointers to
> Doctors.  I want to initialize all the slots to "null", which means
> "this slot is empty now".  Conceptually, the array elements should be
> initialized to null, whereas the My_Doctor field of Patient should be
> initialized to "Don't touch this!".

Wait a minute! In this case, null is both a reasonable initial value, and
a reasonable initial default value. Therefore, it is also not a valid example.

> I would write ":= (others => null);" on the array, to signify that I
> intend to *depend* upon the initial null value, but I would *not* write
> ":= null" on the My_Doctor component of type Patient, to signify that I
> intend to always explicitly initialize it.

Now, consider both of the following:

1. When I read the Patient data structure, how important to me is it to know
that Doctor should never be null? Wouldn't this be better as a comment (or even an 
assertion) within the code which creates Patients, since that's probably
where I need to know this information?

2. Suppose I change the application slightly, such that a Patient is created
when they enter the waiting room. In this case, it may be quite valid for a
Patient to exist, but not have an associated Doctor. In order to maintain
your coding convention, the maintainer must go back to the Patient record and
add an explicit initialization. Is this likely to happen, particularly if the
code is separated from the data structure, and nothing will happen functionally
if the maintainer fails to do this? Or will your coding convention "drift" over
time, causing more confusion?

So, here's my summary:

1. The convention is not self-evident. It has to be explained somewhere. If the 
maintainer fails to get that understanding, it will cause confusion while (s)he 
searches for the meaning to this "useless" code.

2. Even if the convention is understood, its value is hampered by the fact that
it doesn't convey information at the point where it is needed (the algorithm using 
the data structure).

3. As a corollary to #2, further maintenance of the code makes it easy for the
coding convention to be inconsistently applied. This further obfuscates the code.

4. Because it is "active" code which the compiler analyzes (unlike a comment), 
there is also the danger (admittedly slight) of a coding error being introduced.

Overall, I stand by my original statement: I don't see the attraction of this 
style.

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-08  0:00                   ` Ken Garlington
@ 1996-05-08  0:00                     ` Robert A Duff
  1996-05-09  0:00                       ` Ken Garlington
  0 siblings, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-08  0:00 UTC (permalink / raw)



In article <31909E74.1E09@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>Robert A Duff wrote:
>> 
>> Well, sort of.  The use of an invalid representation will not
>> necessarily be detected in all cases.
>
>In what case would 'Valid not detect an invalid representation?

Heh?  I said, "all cases".  The 'Valid attribute is *one* case.  I
meant, pragma Normalize_Scalars will not cause every read of an
uninitialized scalar to be detected.  Am I confused?  I thought we were
talking about pragma Normalize_Scalars, not the 'Valid attribute.

>For scalar X, X'Valid "yields True if and only if the object
>denoted by X is normal and has a valid representation."

Yes.  But be careful -- anything that's erroneous will negate that rule.

>Certainly, if there is _no_ invalid representation of X, then
>'Valid won't detect any. However, you should be able to use 'Size
>to force the existence of an invalid representation for any
>scalar, right?

No, I don't think so.  An implementation might allow it, but where does
the RM require an implementation to accept such a Size clause?  (Note:
The ARG is considering some AI's related to this issues, notably AI's 51
and 109, in case you care to comment.)

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-07  0:00                 ` Robert A Duff
@ 1996-05-08  0:00                   ` Ken Garlington
  1996-05-08  0:00                     ` Robert A Duff
  0 siblings, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-05-08  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> Well, sort of.  The use of an invalid representation will not
> necessarily be detected in all cases.

In what case would 'Valid not detect an invalid representation?
For scalar X, X'Valid "yields True if and only if the object
denoted by X is normal and has a valid representation."

Certainly, if there is _no_ invalid representation of X, then
'Valid won't detect any. However, you should be able to use 'Size
to force the existence of an invalid representation for any
scalar, right?

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-08  0:00             ` Ken Garlington
@ 1996-05-09  0:00               ` Robert A Duff
  1996-05-10  0:00                 ` Robert A Duff
  1996-05-13  0:00                 ` Ken Garlington
  0 siblings, 2 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-09  0:00 UTC (permalink / raw)



In article <3190A8D3.3D53@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>Robert A Duff wrote:
>> 
>> Let me ask you this:  Suppose Ada had a rule that all integers were
>> default-initialized to T'First, where T is the subtype of the variable.
>> Would you be happy with that language?  Would it change the way you
>> write code?
>
>I would be happy with that language (ignoring the resource utilization
>effects), and any time I had an integer that I needed to initialize to
>T'First, I would leave off the initialization (knowing that it was going
>to be set to T'First anyway). Why not?

OK, this explains our disagreement.  I would *not* be happy with such a
language.  You would, and would rely on default-initialization to
T'First.  IMHO, that just masks real bugs in the program.  IYHO, it is
helpful, in that it avoids extra "useless" code.  This is a
philosophical difference that explains all the differences in detail
(which I argue about endlessly below;-)).

Let me be quite clear about what I would *like* in a language: Any type
that's built in to the language, and is treated as a non-composite
entity, should have run-time detection of uninit vars.  This includes
integers, floats, pointers, etc.  It might even include strings,
depending on the language.  (Note: one could view floats as a record
containing exponent, mantissa, etc.  Ada doesn't do that, it views the
whole thing together as a number, so that thing taken as a whole should
be subject to uninit var detection.)

A compiler that detects such errors at compile time, in some cases, is
nice, but we know (halting theorem and all that), that we can't do it in
all cases, so run-time detection is the best we can do, in general.

If this run-time detection causes efficiency problems, or problems in
interfacing to C, or to some hardware, or to anything else, the
*programmer* (not the language designer) can turn it off.

If I'm coding in a language that doesn't support the above ideal, then
I'll probably code in a way *as* *if* the above were true, assuming
that's safe (i.e. assuming that my assumptions are a subset of what's
actually required by the language).

>> >However, I think you've missed my point. Let me retry: Do you have a block
>> >of comments in each compilation unit that says:
>> >
>> >-- NOTE: When a declaration using an access type has an explicit initial
>> >-- value of null, this means that the declared object can be used without
>> >-- any further initialization.
>> 
>> No, of course not.  It only needs to be stated once.
>
>Where?

Wherever all the other project-wide information goes.  Come on Ken,
don't you have *any* project-wide information that needs to be
understood by all programmers who wish to modify the code?  Do you
really expect a programmer to jump in to the middle of the code, grab
some random module, and start hacking on it without knowing anything
about the complete product of which it is a part?  It seems like your
arguments here could be applied to *any* coding conventions -- do you
really want to say that coding conventions are evil, and that any code
that obeys the language standard should be OK?

>> And there are lots
>> of other things that need to be stated once, and you need to give the
>> reader some way of easily finding that place.
>
>Could you give some examples?

E.g., "This project obeys the Ada Quality and Style Guide, which may be
found at <so-and-so>."?  Clearly, anybody meddling with the code
*anywhere* in this project needs to know this fact.  But documenting it
in every source file seems like overkill.  You've got to have a central
place to put these interesting facts.

>> I freely admit that if
>> somebody doesn't understand the coding convention, it won't help them
>> understand the code.  (It probably won't hurt, either, though.)
>
>I disagree. Any time I maintain code that does something non-obvious, I have to 
>stop and figure out why the code was written in the strange manner. (Did
>he really just set that pointer to null, or did I misread "null"?)

OK, fair enough.  But still, before meddling with the code on *my*
project, you will be required to read the document that talks about
project-wide conventions, and you will be required to obey those
conventions (or else, if you think they're stupid, get them changed at
the project level, rather than going off on your own and hacking however
you like).

>> In this example, I meant that it is safe to pretend that Ada doesn't
>> initialize pointers.  Please re-read what I wrote in this light -- that
>> is, when I said "THIS is safe", I meant "THIS PRETENSE is a safe
>> assumption".
>
>I disagree. Re-read my comments about valid access objects which can be
>named similarly to null. In a more general sense, the more (unnecessary)
>code written, the greater the likelihood of error.

Yawn.  Yes, there's a possibility that somebody will type "nell" when
they meant "null".  This seems like a minor concern, since misspelling
names is *always* a concern.  For integers, if I write "X: Some_Int =
O;", I might have meant 0 instead of O.  Big deal.  So don't declare
integer variables called "O", and don't declare pointers called "nell".

>> This hypothetical not-quite-Ada language was an attempt to answer your
>> comment saying that it is hard to understand *what* the coding
>> convention *is*.  It's *not* hard, if you're capable of imagining such a
>> language.
>
>And you have the context of this discussion. Without your explanation,
>within ready reach of the reader, it is quite difficult to guess the
>magic intent of the explicit initialization to null. At least, it is for
>me.

I agree that, for the coding convention to be useful, it has to be
documented, and anybody meddling with the code has to know where that
documentation is, and read it.

>> It doesn't make sense to answer this hypothetical explanation with,
>> "But, in Ada 95, pointers *are* initialized to null.  The RM says so."
>
>But it does make sense to ask, "How does the reader get this explanation?"
>It's in some "central" place, apparently, but that's all I know so far...
>
>It also does make sense to ask, as I did originally, "Why is this convention
>useful?" I'm still not sure I see that part of the discussion...

Yes, it does make sense to ask *that*.  If the convention is truly
useless, then it's bad to require that extra code.  IMHO, the convention
is useful.  I'll try (again) to explain why, but I assume we're past the
question of "how do you even know what the convention is, i.e., how does
the programmer know when to put ':= null'".

>> By the way, all your examples concerned local variables.  The more
>> interesting cases are package-body variables, and record components.
>
>Then present an example  of either type, showing why this convention conveys
>useful information.

I did -- the doctor/patient thing.  You didn't buy it.

>> No, no, no.  That's not what I meant about "safe".  Of course implicit
>> null's are just as safe as explicit nulls.
>
>Safer, IMHO.

This explains why you like my hypothetical language that initializes all
integers to T'First.

>> It just seems to me that it's valuable to know whether a given value is
>> going to be used.  It makes the code more readable, IMHO.
>
>I guess I would be more convinced with an actual example.
>
>Does this approach extend to other cases? For example, suppose a record
>object is being assigned with an aggregate, and some of the aggregate values
>will be used in the code, and others will be overwritten first. Do you have
>some mechanism for distinguishing them?

Good question.  Ada doesn't do detection of uninit vars, so it's not
surprising that aggregates have to be complete, even though some parts
will never be used.  Yes, I *do* think it would be valuable to have that
information in the source code, but Ada doesn't provide any way to say
that, except as a comment.  This is no big deal -- every language has
some unexpressible thing, which is why every language has comments.

>What about loops? When I write "for J in Buffer'Range loop", there's
>an implicit initialization of J to Buffer'First. Is there an issue here?

No, there's no issue here.  It is quite clear (and explicit) what J is
initialized to.  Actually, you state it wrong: J is *not* initialized to
Buffer'First, and then incremented through the loop.  That's true at the
implementation level, but at the semantic level, the right way to think
about it is that a new J is created, each time through the loop, and
this J is constant, and destroyed at the end of that particular
iteration.

>It just seems like a strange way to represent data flow information.

You prefer comments, I guess.

>> In this case, the Ada *language* is what violates the KISS principle.  I
>> understand the reasons behind it, but still, the rule is that some
>> variables get a "junk" initial value, and some variables get a
>> well-defined initial value (by default, of course), which is obviously
>> more complicated than treating all variables the same.
>
>However, if you want all (scalar) variables to get a well-defined default
>initial value, you can do that in Ada 95 with Normalize_Scalars (and 'Size),
>right? I don't know why this is critical for you, but it's there if you want
>it...

Pragma Normalize_Scalars is nice, but it doesn't go far enough, for my
taste, because (1) it doesn't work when there are no "extra bits", and
(2) it doesn't require every read of an uninit scalar to be detected --
the program has to actually do something that trips over that value, and
causes some other constraint check to be violated.  And, of course, it
can't detect uninitialized access values, because any access value that
is conceptually uninitialized is actually set to null by the
implementation, thus masking any such bugs.

>> I claim that the coding convention we're arguing about is actually
>> *more* KISS, since it treats integers and pointers alike.
>
>I disagree that consistency necessarily equates to simplicity.

This is a huge difference in our philosophies.  I won't quite say
"necessarily", but "usually", consisistency = simplicity.  This is
certainly one such case.

>(See the example below).
>
>> >I understand that you want pointers to act like integers. I just don't
>> >understand _why_, since pointers are conceptually different from integers.
>> 
>> I don't see any way in which integers and pointers are different with
>> respect to *this* issue (whether or not they should be initialized).
>
>Well, assuming you don't resort to Unchecked_Conversion or something nasty
>like that, any cases I can think of where you can use integers to reference
>a memory location will automatically have bounds (e.g., as an array reference).
>Pointers are unbounded in Ada 95, as far as I can tell -- there is no easy
>way to check for "in range" in a machine-independent fashion. Therefore,
>to avoid illegal references, you have to do something special, right?

Ada 95 had to *add* some rules to achieve this, for integers.  In Ada
83, "A(I) := 3;" will overwrite random memory if I is uninitialized.  In
Ada 95, it will either raise an exception, or overwrite some random
component of A.  Better than nothing, I suppose.  The rule for "Ptr.all
:= 3;" could be the same, except that Ada 83 already said you can rely
on Ptr being initialized to null.

>> >Here's a different example: Suppose you saw code that looked like:
>> >
>> >  X : Integer := Init (Thousands => 1, Hundreds => 4, others => 0)
>> >                                             -- set X to 1400.
>> >
>> >Would this be a preferred approach, since I'm just creating numeric literals
>> >the way I create array aggregates?
>> 
>> Sorry, but you lost me there.
>
>To slightly misquote a famous man: "I claim that the coding convention I'm 
>proposing is actually *more* KISS, since it treats integers and arrays alike....
>I don't see any way in which integers and arrays are different with
>respect to *this* issue (how they should be initialized)."

I don't know of any *famous* man that said anything like that.  ;-)

I guess I see what you're getting at -- if integers are like pointers
with regard to initialization, then all types should be like all other
types, with regard to aggregates.  Seems like a bogus argument, to me.
I said integers and pointers should behave the same with respect to
initialization.  Composites should not.  I certainly didn't say that
integers should be initialized using aggregates.

>However, applying my guideline that you don't write extra code unless there's
>a clear purpose, this coding convention for integer initialization is silly.
>It doesn't communicate, it obfuscates.

I still don't see how you translate a requirement to initialize, into a
requirement to initialize with a weird aggregate with Hundreds
components and so forth.

>> >> But initializing all access values to null does
>> >> *not* constitute a "reasonable" initial value, in many cases.
>> >
>> >This is certainly true. However, can you identify a case where null _is_ a
>> >reasonable initial value, but not a reasonable initial _default_ value?
>> 
>> Sure.  I have a hard time imagining a type that is otherwise.
>> 
>> Suppose (stealing from another thread;-)) we have Patients and Doctors.
>> Each Patient is represented by a record, containing (among other
>> things), a pointer to its Doctor.  Because of the way this application
>> works, you can't create a Patient without giving it a Doctor -- i.e.
>> there is no reasonable default value for the Doctor field, and it must
>> always be non-null.
>
>Wait a minute! In this case, null is neither a reasonable initial value, nor
>a reasonable initial default value. Therefore, it is not a valid example.
>
>> However, there's a hash table, implemented as an array of pointers to
>> Doctors.  I want to initialize all the slots to "null", which means
>> "this slot is empty now".  Conceptually, the array elements should be
>> initialized to null, whereas the My_Doctor field of Patient should be
>> initialized to "Don't touch this!".
>
>Wait a minute! In this case, null is both a reasonable initial value, and
>a reasonable initial default value. Therefore, it is also not a valid example.

But default values are per type.  You can't say, "wait a minute", null
should be the default but null should not be the default.  We have a
single type here -- the language has to define that it is
default-initialized to null, or that it is not default-initialized to
null.  My point is that for one particular type, we want *some* things
initialzied to null, and we want *some* things to be initially undefined
(and we wish our compiler would detect any errors).  Of course, we might
well want *some* things to be initialized to

    := The_Default_Doctor_Assigned_By_The_HMO;

>> I would write ":= (others => null);" on the array, to signify that I
>> intend to *depend* upon the initial null value, but I would *not* write
>> ":= null" on the My_Doctor component of type Patient, to signify that I
>> intend to always explicitly initialize it.
>
>Now, consider both of the following:
>
>1. When I read the Patient data structure, how important to me is it to know
>that Doctor should never be null? Wouldn't this be better as a comment (or even an 
>assertion) within the code which creates Patients, since that's probably
>where I need to know this information?

As a comment?  Surely you're not going to claim that comments are more
reliable than coding conventions?!

As an assertion when the Patient is created?  No, it's an *invariant*,
which needs to be understood by the person writing the code to create
one, and also by the person writing the code to look at one.  When
writing code, you need to know whether
"Sue_Doctor(Some_Patient.My_Doctor)" is valid -- you need to know
whether you have to write:

    if Some_Patient.My_Doctor = null then
        Dont_Bother; -- Patient was self-medicating; noone to blame.
    else
        Sue_Doctor(Some_Patient.My_Doctor);
    end if;

instead.

>2. Suppose I change the application slightly, such that a Patient is created
>when they enter the waiting room. In this case, it may be quite valid for a
>Patient to exist, but not have an associated Doctor. In order to maintain
>your coding convention, the maintainer must go back to the Patient record and
>add an explicit initialization. Is this likely to happen, particularly if the
>code is separated from the data structure, and nothing will happen functionally
>if the maintainer fails to do this? Or will your coding convention "drift" over
>time, causing more confusion?

If the application is different, then yes, you have to change the code.
If you used comments, instead, then you'd have to change the comments.
Either way, there's a danger that the programmer will forget to do it.
This is the nature of comments, which is the same nature as un-enforced
coding conventions.  I don't see any way around that, except to outlaw
both comments and coding conventions.

>So, here's my summary:
>
>1. The convention is not self-evident. It has to be explained somewhere. If the 
>maintainer fails to get that understanding, it will cause confusion while (s)he 
>searches for the meaning to this "useless" code.

How is this different from any other coding convention?  Do you claim
that coding conventions are always evil, because of this problem?

>2. Even if the convention is understood, its value is hampered by the fact that
>it doesn't convey information at the point where it is needed (the algorithm using 
>the data structure).

No algorithm using a data structure can be understood without looking at
the type declarations involved.  This is just one more such case -- if
you see "Some_Component: Some_Pointer := null;", that tells you that you
can (and should) write an algorithm that reads Some_Component before
setting it.

>3. As a corollary to #2, further maintenance of the code makes it easy for the
>coding convention to be inconsistently applied. This further obfuscates the code.

Agreed.  What's the alternative?  Comments?  Same maintenance problem.
Don't bother?  Well, there is some useful information here, do you
really want to hide it from the users of this type?

>4. Because it is "active" code which the compiler analyzes (unlike a comment), 
>there is also the danger (admittedly slight) of a coding error being introduced.
>
>Overall, I stand by my original statement: I don't see the attraction of this 
>style.

I guess we'll have to agree to disagree.  If you work on *my* project,
you'll have to obey *my* conventions.  If I work on *your* project I
will, of course, obey your conventions.  Either way, the maintainer can
benefit from knowing what the coding conventions are.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-08  0:00                     ` Robert A Duff
@ 1996-05-09  0:00                       ` Ken Garlington
  1996-05-09  0:00                         ` Robert A Duff
  0 siblings, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-05-09  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> Heh?  I said, "all cases".  The 'Valid attribute is *one* case.  I
> meant, pragma Normalize_Scalars will not cause every read of an
> uninitialized scalar to be detected.  Am I confused?  I thought we were
> talking about pragma Normalize_Scalars, not the 'Valid attribute.

You use Normalize_Scalars with 'Valid to test for uninitialized scalars.
At least, that's the way I would do it!

Sort of like using default initialization of access objects with comparison
to mull (or by attempting a dereference, if you want the implicit
exception instead of raising your own). The initialization by itself
doesn't do anything magical, and certainly the value "null" for access
values doesn't cause every read of a null access value to be "detected"
(exception raised?).

> >For scalar X, X'Valid "yields True if and only if the object
> >denoted by X is normal and has a valid representation."
> 
> Yes.  But be careful -- anything that's erroneous will negate that rule.

I guess my understand of erroneous (and maybe this is an Ada 83-only
understanding) is that erroneous code pretty much negated _all_ Ada rules.
If you want to reliably detect the results of erroneous code, you have to
step outside the standard, right?

> 
> >Certainly, if there is _no_ invalid representation of X, then
> >'Valid won't detect any. However, you should be able to use 'Size
> >to force the existence of an invalid representation for any
> >scalar, right?
> 
> No, I don't think so.  An implementation might allow it, but where does
> the RM require an implementation to accept such a Size clause?  (Note:
> The ARG is considering some AI's related to this issues, notably AI's 51
> and 109, in case you care to comment.)

Since I don't generally get to see AIs, I can't comment on them. However,
my (possibly naive) reading of RM95:13.3:39-43 is that I can always cause an
object to use more than the minimum amount of bits. I suspect that there
may not be a _portable_ definition of Size in all cases, but it at least seems
there should be _some_ value that would work for any given compiler.

> 
> - Bob

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-09  0:00                       ` Ken Garlington
@ 1996-05-09  0:00                         ` Robert A Duff
  0 siblings, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-09  0:00 UTC (permalink / raw)



In article <3191E6E3.453@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>You use Normalize_Scalars with 'Valid to test for uninitialized scalars.
>At least, that's the way I would do it!

No.  You don't check every use of every scalar using 'Valid.  The 'Valid
attribute is for when you've got some data from outside Ada (hardware,
other language,...).

>I guess my understand of erroneous (and maybe this is an Ada 83-only
>understanding) is that erroneous code pretty much negated _all_ Ada rules.
>If you want to reliably detect the results of erroneous code, you have to
>step outside the standard, right?

That's right.  Ada 83 and Ada 95 have the same definition of erroneous
-- anything could happen.  And that overrides any rule to the contrary.
My point was just that if you ask X'Valid? you had better ensure that
you're not doing something erroneous in the evaluation of X (which is a
danger).

>Since I don't generally get to see AIs, I can't comment on them. However,
>my (possibly naive) reading of RM95:13.3:39-43 is that I can always cause an
>object to use more than the minimum amount of bits. I suspect that there
>may not be a _portable_ definition of Size in all cases, but it at least seems
>there should be _some_ value that would work for any given compiler.

The AI's are publicly available:

    ftp://sw-eng.falls-church.va.us/public/AdaIC/standards/95com/ada-issues/

And your comments on them are welcome -- send e-mail to the address
mentioned at the front of the RM.  There will be an ARG meeting in
mid-June, by the way.

What do you make of 13.1(20)?

I think that your belief, that you can always force an implementation to
use an extra bit, is wrong.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-10  0:00                     ` Robert A Duff
@ 1996-05-10  0:00                       ` Ken Garlington
  1996-05-11  0:00                         ` Robert A Duff
  1996-05-11  0:00                         ` David Kristola
  0 siblings, 2 replies; 52+ messages in thread
From: Ken Garlington @ 1996-05-10  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> By the way, in many cases, I do indeed use a special name for null for
> each access type.  I declare "No_Particular_Doctor: Doctor_Ptr := null;"
> or something like that, and always use No_Particular_Doctor instead of
> null.

Given that you don't really use the _literal_ "null", I can see where your coding
technique adds readability - in particular, if you define Doctor as its
own data abstraction class, such as

  package Doctor is

    type Object is ...;
    type Reference is private;

    function New_Reference ( With_Contents : Object ) return Reference;
    function Contents ( Of_Reference : Reference ) return Object;
    function No_Particular_Doctor return Reference;

    Reference_Access_Error : exception;  -- contents of uninitialized Reference
    Undefined_Doctor_Error : exception;  -- contents of Reference = No_Particular_Doctor
    Object_Storage_Error   : exception;  -- unable to create new Reference

  private
    type Reference is access Object;
  end Doctor;

(I suppose you could also make this a generic instantiation, with a little work.)

However, in my mind, you're no longer talking about the desirability of
using explicit null -- you're talking about the desirability of using meaningful
names for a use of null, and potentially hiding the fact that you're using _any_ 
particular underlying type (an access type). Certainly, once you have Doctor.Reference
as a private type, it should be easy to convert from access values to integers -
in fact, ideally, you don't have to change any of the code that declares or uses 
Doctor_Ptr objects! (You could also readily switch to access values outside of
the heap, for that matter.)

I think there's something subtle here. In my mind, saying "I don't want to
_use_ the literal null, since I want to reserve that to mean 'uninitialized'"
makes some sense. I feel that's different from saying, "I want an explicit
_initialization_ of the literal null to mean something different than the
implicit _initialization_ to null."

> (Same problem with any other coding convention -- if you don't
> obey it, you'll end up with a mixture of No_Particular_Doctor and null,
> causing confusion.)

Of course, depending on how you write the Doctor abstraction, you can
_enforce_ this coding convention, and avoid any possible confusion.

> No, I wouldn't do that [directly preserve access_check behavior].  I would
> rely on pragma Normalize_Scalars, or
> else rely on the normal way of detecting uninitialized integers -- be
> careful.

Well, if you do the former, you still might have to visit each unitialized
case to ensure that a valid out-of-range value exists (although in this
example, it's a pretty sure bet.) You would also have to add 'Valid to get
reliable checking, so I'm not sure I see the advantage of just adding the
pragma.

As far as the latter case, you could make the same argument for your coding
standard -- instead of providing hints to the user, you could just admonish
them to "be careful." It seems to me that, if the explicit initialization is
to mean something, then the lack of explicit initialization should mean something,
as well, and that this meaning should be carried forward in the translated
software.

> If I really did want to encode detection of the uninit integers in the
> program, I would use a different value than the one meant to encode
> "null".  To really get that right, you'll have to wrap the integer in a
> record, since only records allow user-defined default initial values
> (unfortunately).  And that might well be too much trouble.  It might
> even introduce bugs, since access types and integers are always passed
> by copy, whereas records might be passed by reference.

On the other hand, if you export No_Particular_Doctor from the Doctor
class, you could go further and do the following:

(1) When it's implemented as a pointer, have No_Particular_Doctor be
implemented as an access to some dummy value. This way, you could use null
to mean uninitialized, and No_Particular_Doctor to mean "no doctor assigned."

(2) When it's implemented as an integer, have No_Particular_Doctor be
an in-range value, but a never-used entry in your table. That way, out-of-range
values (from Normalize_Scalars) would mean uninitialized, and No_Particular_Doctor
would mean the same as in #1.

This, to me, would make sense. However, this is not a "coding standard" to my
thinking - this is a design abstration technique. I agree that the abstraction
principle is quite valid, and very powerful (particularly in Ada, where it can
be enforced as part of the contract model).

> No such tool is going to do a decent job when it comes to heap data.

I'm not sure that the tool has to do a particularly thorough job, just to
find out the information you're attempting to convey with the literal null.
However, we can agree to disagree on this.

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-09  0:00               ` Robert A Duff
@ 1996-05-10  0:00                 ` Robert A Duff
  1996-05-10  0:00                   ` Ken Garlington
  1996-05-13  0:00                 ` Ken Garlington
  1 sibling, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-10  0:00 UTC (permalink / raw)



In article <Dr5H2B.5FD@world.std.com>,
I <bobduff@world.std.com> wrote:
> [stuff about explicitly initializing to null]

Ken,

Here's a real practical example, which might be more convincing than
vague notions of readability:

I have a program that uses access types.  I want to convert to using
integers, where the integers point into an array.  There are several
reasons you might want to do that.  E.g. if you know you only have a
small number of heap objects, you might be able to make the integer
smaller than an access value.  It might make it easier to manage your
heap in some way that is not directly supported for access types.

If you followed the convention of always explicitly initializing access
types (either to null or to whatever the right value should be), then
this conversion can be fairly mechanical -- change "Foo.all" to
"Heap(Foo)", where Heap is the array representing the heap.  Change
"null" to "Null_Pointer", where Null_Pointer is a constant initialized
to an integer number representing null, and so on.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-10  0:00                 ` Robert A Duff
@ 1996-05-10  0:00                   ` Ken Garlington
  1996-05-10  0:00                     ` Robert A Duff
  0 siblings, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-05-10  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> Here's a real practical example, which might be more convincing than
> vague notions of readability:
> 
> I have a program that uses access types.  I want to convert to using
> integers, where the integers point into an array.  There are several
> reasons you might want to do that.  E.g. if you know you only have a
> small number of heap objects, you might be able to make the integer
> smaller than an access value.  It might make it easier to manage your
> heap in some way that is not directly supported for access types.
> 
> If you followed the convention of always explicitly initializing access
> types (either to null or to whatever the right value should be), then
> this conversion can be fairly mechanical -- change "Foo.all" to
> "Heap(Foo)", where Heap is the array representing the heap.  Change
> "null" to "Null_Pointer", where Null_Pointer is a constant initialized
> to an integer number representing null, and so on.

I thought I was convinced by this, but on reflection...

1. Are you converting _all_ access types to encoded integers, or just a
particular _use_ of an access type? If the latter, then you can't do it completely
mechanically (assuming more than one access type) for all occurences of null.
You still have to find the specific declarations that need to be modified. Since you
have to do that anyway, you're really not gaining much by denoting them with an 
explicit null. Now, if you had a different name for null for each access type, this 
might work. Otherwise, I don't think you gain very much.

2. Assuming you really _are_ converting all access types to integers (or even
if you're not, now that I think about it), you have a different problem. To retain
your (presumably desired) behavior of an implicit null being used to catch errors,
you also have to come up with some way to trap references to your uninitialized 
integers. This means you have to visit those declarations with the implicit null,
and either assign them an out-of-range value (Null_Pointer, maybe?), or do something 
else. (You probably will also want to add the appropriate assertions in your code 
before you "dereference" the integers. However, even if you didn't add the code, the 
Null_Pointer assignment would still be useful for debugging purposes...)

3. Both of these actions (replacing the explicit null with Null_Pointer, and the 
implicit null with... Null_Pointer?!?) can be done mechanically (and with less human 
error, I suspect), without the explicit null - assuming you have a tool that does data 
flow analysis, or at least can track declaration/use information. In fact, the explicit 
null could actually reduce the effectiveness of such an analysis. I know that, 
particularly with older code that has been maintained by several parties, I would trust 
an automated analysis tool over your coding convention.

So, I'm still not convinced.

> 
> - Bob

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-10  0:00                   ` Ken Garlington
@ 1996-05-10  0:00                     ` Robert A Duff
  1996-05-10  0:00                       ` Ken Garlington
  0 siblings, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-10  0:00 UTC (permalink / raw)



In article <31933B50.687D@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>I thought I was convinced by this, but on reflection...

>1. Are you converting _all_ access types to encoded integers, or just a
>particular _use_ of an access type? If the latter, then you can't do it
>completely mechanically (assuming more than one access type) for all
>occurences of null.  You still have to find the specific declarations
>that need to be modified. Since you have to do that anyway, you're
>really not gaining much by denoting them with an explicit null. Now, if
>you had a different name for null for each access type, this might
>work. Otherwise, I don't think you gain very much.

I imagined converting a particular access type, or perhaps a group of
related access types in a particular part of the program.  I didn't mean
I would replace all occurrences of ":= null" with ":= Null_Integer", or
whatever -- I realize you have to pay attention to what the types are.
And you'll search the particular parts of the program that deal with
this (those) type(s).

By the way, in many cases, I do indeed use a special name for null for
each access type.  I declare "No_Particular_Doctor: Doctor_Ptr := null;"
or something like that, and always use No_Particular_Doctor instead of
null.  (Same problem with any other coding convention -- if you don't
obey it, you'll end up with a mixture of No_Particular_Doctor and null,
causing confusion.)

>2. Assuming you really _are_ converting all access types to integers
>(or even if you're not, now that I think about it), you have a
>different problem. To retain your (presumably desired) behavior of an
>implicit null being used to catch errors, you also have to come up with
>some way to trap references to your uninitialized integers. This means
>you have to visit those declarations with the implicit null, and either
>assign them an out-of-range value (Null_Pointer, maybe?), or do
>something else. (You probably will also want to add the appropriate
>assertions in your code before you "dereference" the integers. However,
>even if you didn't add the code, the Null_Pointer assignment would
>still be useful for debugging purposes...)

No, I wouldn't do that.  I would rely on pragma Normalize_Scalars, or
else rely on the normal way of detecting uninitialized integers -- be
careful.

If I really did want to encode detection of the uninit integers in the
program, I would use a different value than the one meant to encode
"null".  To really get that right, you'll have to wrap the integer in a
record, since only records allow user-defined default initial values
(unfortunately).  And that might well be too much trouble.  It might
even introduce bugs, since access types and integers are always passed
by copy, whereas records might be passed by reference.

>3. Both of these actions (replacing the explicit null with
>Null_Pointer, and the implicit null with... Null_Pointer?!?) can be
>done mechanically (and with less human error, I suspect), without the
>explicit null - assuming you have a tool that does data flow analysis,
>or at least can track declaration/use information. In fact, the
>explicit null could actually reduce the effectiveness of such an
>analysis. I know that, particularly with older code that has been
>maintained by several parties, I would trust an automated analysis tool
>over your coding convention.

No such tool is going to do a decent job when it comes to heap data.

>So, I'm still not convinced.

Sorry.  ;-)

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-10  0:00                       ` Ken Garlington
  1996-05-11  0:00                         ` Robert A Duff
@ 1996-05-11  0:00                         ` David Kristola
  1996-05-11  0:00                           ` Robert A Duff
  1 sibling, 1 reply; 52+ messages in thread
From: David Kristola @ 1996-05-11  0:00 UTC (permalink / raw)




I apologize if this has already been brought up.  I missed it if it was.

You could always create a "bad" value to initialize pointers to, if null
is an acceptable alternative.  By this i mean something like:

   type Object is...
   type Reference is access Object;

   Bad_Reference : constant Reference := new Object; -- possibly with a "bad" bit set.

   ...

   procedure Do_Something is

      Pointer : Reference := Bad_Reference;

   begin
      ...
      if Pointer = Bad_Reference then
         ...
      elsif Pointer = null then

In Ada95, you can even have Bad_Reference pointing to a static object.

---

david kristola
Work: davidk@os1.ese.lmsc.lockheed.com
Play: DJKristola@aol.com





^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-11  0:00                         ` David Kristola
@ 1996-05-11  0:00                           ` Robert A Duff
  0 siblings, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-11  0:00 UTC (permalink / raw)



In article <4n0lqa$9dt@butch.lmsc.lockheed.com>,
David Kristola <davidk@os1.ese.lmsc.lockheed.com> wrote:
>      if Pointer = Bad_Reference then
>         ...
>      elsif Pointer = null then
>
>In Ada95, you can even have Bad_Reference pointing to a static object.

Sure, but the point is to avoid *accidental* use of a bad reference.
If I write "X := Y;", and Y is Bad_Reference, I won't get any exception.
But I would like to.

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-10  0:00                       ` Ken Garlington
@ 1996-05-11  0:00                         ` Robert A Duff
  1996-05-11  0:00                         ` David Kristola
  1 sibling, 0 replies; 52+ messages in thread
From: Robert A Duff @ 1996-05-11  0:00 UTC (permalink / raw)



In article <31937B57.5B0@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>I think there's something subtle here. In my mind, saying "I don't want to
>_use_ the literal null, since I want to reserve that to mean 'uninitialized'"
>makes some sense.

That's probably a better idea.  With tagged types, this becomes
convenient -- you have an "access to T'Class", and you have a special
derivative of T that means "None" or whatever, and point to that.
Reserve null to mean "Bad".  Use access parameters whenever possible
(since access parameters do a run-time check that the thing isn't null.)

>Well, if you do the former, you still might have to visit each unitialized
>case to ensure that a valid out-of-range value exists (although in this
>example, it's a pretty sure bet.) You would also have to add 'Valid to get
>reliable checking, so I'm not sure I see the advantage of just adding the
>pragma.

You don't have to use 'Valid to get usefulness out of pragma
Normalize_Scalars.  *Most* uses of a scalar variable will involve some
sort of constraint check, and Normalize_Scalars will ensure that these
checks fail for uninit vars.  Sad that it's not *all* uses, but that's
for efficiency.

The 'Valid attribute is for checking data that comes in from the outside
world.  You isolate this code, and use 'Valid before dealing with the
data.  'Valid is *not* for detecting uninitialized vars -- those happen
by accident, and are potentially scattered throughout the code.  So any
mechanism that requires the programmer to do something explicit (like
ask whether 'Valid) misses the point.

>I'm not sure that the tool has to do a particularly thorough job, just to
>find out the information you're attempting to convey with the literal null.
>However, we can agree to disagree on this.

OK.  :-)

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-09  0:00               ` Robert A Duff
  1996-05-10  0:00                 ` Robert A Duff
@ 1996-05-13  0:00                 ` Ken Garlington
  1996-05-13  0:00                   ` Robert A Duff
  1996-05-13  0:00                   ` Ken Garlington
  1 sibling, 2 replies; 52+ messages in thread
From: Ken Garlington @ 1996-05-13  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> OK, this explains our disagreement.  I would *not* be happy with such a
> language.  You would, and would rely on default-initialization to
> T'First.  IMHO, that just masks real bugs in the program.

The part that perplexes me about this statement (other than not being able
to figure out what kind of "bugs" are "masked"), is that it only seems to
apply to the implicit action that occurs when an access type is declared.
For example, do you also avoid the following constructs? If not, why not?

1. Use of "others" in an aggregate expression, case statement, etc.

2. Controlled types

3. Dispatching

4. Use of default values for components of a record type.

You did say:

> Good question.  Ada doesn't do detection of uninit vars, so it's not
> surprising that aggregates have to be complete, even though some parts
> will never be used.  Yes, I *do* think it would be valuable to have that
> information in the source code, but Ada doesn't provide any way to say
> that, except as a comment.

Why not have a coding convention that says, "you can only use others to
complete an aggregate?" Thus, if the value is meaningful to the algorithm,
it has to be explicitly identified in the aggregate. Isn't this the same
approach as you're taking for null?

How about requiring that others can only be used for case statements if
there is no action to be taken for the case selector value -- that is,
the "others" branch is not important to the algorithm?

All dispatching would have to be replaced by case statements, of course,
and you'd have to always put the initial value on the object declaration,
never the type.

All of these can be expressed directly in the language, so it's in the
source code, as you desire.

If you are consistent in your approach, then I can at least agree that
you are expressing a philosophy regarding the use of the language ("no
implicit actions, since they hide bugs." If you're not consistent,
then I don't understand why the philosophy makes sense in some cases,
but not others.


>  IYHO, it is
> helpful, in that it avoids extra "useless" code.  This is a
> philosophical difference that explains all the differences in detail
> (which I argue about endlessly below;-)).

However, I think it's "philosophy" that's backed up by experience in
maintaining code. Have you done experiments that showed the value of adding
extra code in this case?

> Let me be quite clear about what I would *like* in a language: Any type
> that's built in to the language, and is treated as a non-composite
> entity, should have run-time detection of uninit vars.

OK. Why do you feel that you can't achieve this in Ada? With Ada 95, you
have the ability to detect uninitialized scalars at any point in the program,
so long as the compiler can generate a "marker" value for the object (and
it supports the Safety and Security annex, of course).

> If this run-time detection causes efficiency problems, or problems in
> interfacing to C, or to some hardware, or to anything else, the
> *programmer* (not the language designer) can turn it off.

Which you can do in Ada, for everything except access values.

If your argument is that _requiring_ default initialization of access
values can sometimes be inefficient, I guess I'd agree. I don't see how
your coding convention affects that situation, however. (In fact, if using
a dumb compiler, it could lead to _double_ initialization, worsening the
situation.)

> If I'm coding in a language that doesn't support the above ideal, then
> I'll probably code in a way *as* *if* the above were true, assuming
> that's safe (i.e. assuming that my assumptions are a subset of what's
> actually required by the language).

However, to do this consistently in Ada, you need to make a data abstraction
for each type for which you want such protection. Just providing a redundant
initialization fails to meet your goal.

> Come on Ken,
> don't you have *any* project-wide information that needs to be
> understood by all programmers who wish to modify the code?

With respect to coding standards? I would expect that a maintainer can
read and understand my code without reference to coding standards.
Furthermore, I would expect that my code would still be readable even
if a different set of coding standards were used.

>  Do you
> really expect a programmer to jump in to the middle of the code, grab
> some random module, and start hacking on it without knowing anything
> about the complete product of which it is a part?

Absolutely not. They would need to understand the design and the requirements
from which the code was generated. However, they don't need to know the
coding standards. It may make the code more maintainable in the future if
they do maintain those standards, but the goal should be the use of coding
standards that make it easy to read the code, not code that makes it
necessary to read the coding standards!

>  It seems like your
> arguments here could be applied to *any* coding conventions -- do you
> really want to say that coding conventions are evil, and that any code
> that obeys the language standard should be OK?

Nope - I want to say what I just said: "the goal should be the use of coding
standards that make it easy to read the code, not code that makes it
necessary to read the coding standards!"

> E.g., "This project obeys the Ada Quality and Style Guide, which may be
> found at <so-and-so>."?  Clearly, anybody meddling with the code
> *anywhere* in this project needs to know this fact.

Would that be the 83 standard, or the 95 standard?

I would hope that someone could use the 95 standard to maintain my code,
whether or not I used the 95 standard (or the 83 standard) to develop it.

More importantly, I would expect that anyone could read (and maintain) code
developed with either AQ&S, without knowledge of the AQ&S!

> But still, before meddling with the code on *my*
> project, you will be required to read the document that talks about
> project-wide conventions, and you will be required to obey those
> conventions (or else, if you think they're stupid, get them changed at
> the project level, rather than going off on your own and hacking however
> you like).

What happens if someone on another project reuses your code?

> Yawn.  Yes, there's a possibility that somebody will type "nell" when
> they meant "null".  This seems like a minor concern, since misspelling
> names is *always* a concern.  For integers, if I write "X: Some_Int =
> O;", I might have meant 0 instead of O.  Big deal.  So don't declare
> integer variables called "O", and don't declare pointers called "nell".

1. If explicit initialization to null provides a minor _benefit_, then
it should be a concern if this benefit is outweighed by a disadvantage, even
if it is a minor one. The fallacy in your integer initialization comparison
is that the minor disadvantage is outweighed by a _major_ benefit - namely
that the code may fail if the variable is uninitialized! (Of course, if
you're initializing integers to zero for no good reason, then it's _also_
a big deal.)

2. Do you have something in your coding standards regarding the naming of
access objects, such that they don't have names similar to null? If not,
do you intend to add such a restriction based on this discussion? What
about tools to check for violations of this new standard?

3. Several little disadvantages (assignment to the wrong value, reader
confusion, etc.) can equal a significant disadvantage.

> Yes, it does make sense to ask *that*.  If the convention is truly
> useless, then it's bad to require that extra code.

Now we're getting somewhere!

You just said that there _would_ be a condition where it would be bad
to require extra code. I think there are several conditions where this
is bad, I've given a couple of examples:

1. Explicit initialization to null.

2. Forcing integer values to look like aggregates (on the spurious
grounds that it makes integer and array initial values "look more
consistent," as you have indicated you desire for integers and
access values.)

So, it seems to me we're left with the argument,"is explicit initialization to null one of 
those conditions?"

Perhaps it would help if you identified cases where you would legitimately
object to adding extra code, and then let's see if I can convince you that
your coding convention matches one of your cases...

> I did -- the doctor/patient thing.  You didn't buy it.

Right, because Ada provides a way to do what you _really_ want to do, and it doesn't
involve use of your coding convention. If you really want to plan for changing
the representation of an access value to an integer, then Ada gives you everything
you need. If you want to have two reserved values for a pointer (either represented as Ada 
access values or integers), then you can do that, too. In fact, if you re-write my example as 
a generic, you can get both features very easily. And, with just a couple of comments on the 
generic explaining what it's for (to permit the internal representation of the pointer to 
change), the reader can easily grasp what's happening, without reference to _any_ coding 
standard. Furthermore, the maintainer is encouraged to continue to use the convention, since 
it's more work to rewrite the code to remove it.

Your current coding convention, on the other hand, seems to be a fairly "weak" (e.g., no 
support from the toolset) and unreliable way to not-quite-achieve what you want. Why not go 
all the way?

(By the way, I misspoke in my example. Since you're only using the integer value to access an 
array, you probably don't need the 'Valid check. You just need to map Constraint_Error into 
whatever you're doing for uninitialized values.)

If you had a coding (or, more likely, a design) convention that said, "Use generic XXX to 
create pointer types, so that (a) two reserved values are available for each pointer and (b) 
pointers representations can be readily changed between Ada access types and other types." I'd 
say that was a reasonable statement, if you believed
either outcome was reasonably likely to happen. In fact, my design standard (ADARTS) supports 
thinking in these terms.

> This explains why you like my hypothetical language that initializes all
> integers to T'First.

No, I was referring to the disadvantages of using explicit nulls to overwrite
implicit nulls. If something is implicitly set in all cases, then it's safer
just to assume this behavior will occur in the source. Less confusion, less
chance for a coding error.

It might be safer if all scalars had out-of-range values. However, my applications
also have to run in real-time, so I would not claim that this is a requirement
for a safety-critical system. Useful, perhaps.

> >What about loops? When I write "for J in Buffer'Range loop", there's
> >an implicit initialization of J to Buffer'First. Is there an issue here?
> 
> No, there's no issue here.  It is quite clear (and explicit) what J is
> initialized to.

What is Buffer'First?

> >It just seems like a strange way to represent data flow information.
> 
> You prefer comments, I guess.

Certainly, comments could explicitly say what you meant to say, and they could
be placed at the point in the code where you need them. However, I was thinking
more in terms of declaration/use information generated by a tool, or better
yet, a graphical representation.

> Pragma Normalize_Scalars is nice, but it doesn't go far enough, for my
> taste, because (1) it doesn't work when there are no "extra bits", and
> (2) it doesn't require every read of an uninit scalar to be detected --
> the program has to actually do something that trips over that value, and
> causes some other constraint check to be violated.  And, of course, it
> can't detect uninitialized access values, because any access value that
> is conceptually uninitialized is actually set to null by the
> implementation, thus masking any such bugs.

(1) You should be able to generate extra bits for any scalar object you want
to check, AFAIK.

(2) Note that, with an appropriate abstraction, you _can_ require every
read of an uninit scalar to be detected. You can also have this abstraction
cover access values.

(3) Also, note that the use of "null" for access values only detects _dereferences_ of uninit 
pointers. You can still read the access object in other ways, without
implicit detection. To cover _all_ cases, you need an abstraction.

(4) Most importantly, how does your coding convention help this situation in any way?

> >> I claim that the coding convention we're arguing about is actually
> >> *more* KISS, since it treats integers and pointers alike.
> >
> >I disagree that consistency necessarily equates to simplicity.
> 
> This is a huge difference in our philosophies.  I won't quite say
> "necessarily", but "usually", consisistency = simplicity.  This is
> certainly one such case.

And you stated for another case of "consistency":

> Seems like a bogus argument, to me.
> I said integers and pointers should behave the same with respect to
> initialization.  Composites should not.

Why not? Usually, consistency = simplicity, right?

Note also that "consistency" in one dimension (making access values look like
integers) can cause inconsistency in another dimension (see my discussion of
extending your philosophy to other areas of the coding standards).

> >Well, assuming you don't resort to Unchecked_Conversion or something nasty
> >like that, any cases I can think of where you can use integers to reference
> >a memory location will automatically have bounds (e.g., as an array reference).
> >Pointers are unbounded in Ada 95, as far as I can tell -- there is no easy
> >way to check for "in range" in a machine-independent fashion. Therefore,
> >to avoid illegal references, you have to do something special, right?
> 
> Ada 95 had to *add* some rules to achieve this, for integers.  In Ada
> 83, "A(I) := 3;" will overwrite random memory if I is uninitialized.
> In Ada 95, it will either raise an exception, or overwrite some random
> component of A.  Better than nothing, I suppose.  The rule for "Ptr.all
> := 3;" could be the same, except that Ada 83 already said you can rely
> on Ptr being initialized to null.

Right - the expression should either write to a
defined, allocated subset of memory (the range of A) or it should raise
an exception. (Is there a case where an uninitialized integer even gets
into the class of a bounded error, or is it always run-time detectable?)

However, in Ada 95, an access value can't have any such subset defined. (In
Ada 83, you could at least argue that it's restricted to the pool.)  Therefore,
I can see where access values should be treated differently. (If null were not
a default initial value, I would assume access to an uninitialized access would
be erroneous, and not even bounded, right?)

> I still don't see how you translate a requirement to initialize, into a
> requirement to initialize with a weird aggregate with Hundreds
> components and so forth.

I translate a requirement to treat access values like integers on initialization,
into a requirement to treat integers like arrays on initialization. Seems very
consistent to me!

> My point is that for one particular type, we want *some* things
> initialzied to null, and we want *some* things to be initially undefined
> (and we wish our compiler would detect any errors).

However, earlier you said something a little different: that you wanted two
reserved values for each access type: one that represented "uninitialized"
and one that represented "no particular value." Furthermore, you wanted all
access types to have a default value of "uninitialized", so that it could
later be detected. Given thew way Ada is designed, you can certainly do that
by mapping "uninitialized" to "null" and "no particular value" to some other (non-null) 
allocated value used only for that purpose. You can't do it by
using null in two different contexts, as far as I can tell. Furthermore, to
get your behavior for "uninitialized", you want the language to do default
initialization to "uninitialized." Ada does that for access values.

can you identify a case where null _is_ a
> >> >reasonable initial value, but not a reasonable initial _default_ value?

> >1. When I read the Patient data structure, how important to me is it to know
> >that Doctor should never be null? Wouldn't this be better as a comment (or even an
> >assertion) within the code which creates Patients, since that's probably
> >where I need to know this information?
> 
> As a comment?  Surely you're not going to claim that comments are more
> reliable than coding conventions?!

I'm not sure how reliability got into this discussion, since your convention isn't
reliable (checked in any way), right? I would certainly claim
that an assertion is more reliable than your coding convention (particularly in the
context of the abstraction I showed), since it _does_ involve an explicit check.

If you meant "readable," then yes - if you gave me the choice of Hungarian notation
or comments, I'd probably choose comments.

> As an assertion when the Patient is created?  No, it's an *invariant*,
> which needs to be understood by the person writing the code to create
> one, and also by the person writing the code to look at one.

Great! _Check_ it as an invariant, then.

  When
> writing code, you need to know whether
> "Sue_Doctor(Some_Patient.My_Doctor)" is valid -- you need to know
> whether you have to write:
> 
>     if Some_Patient.My_Doctor = null then
>         Dont_Bother; -- Patient was self-medicating; noone to blame.
>     else
>         Sue_Doctor(Some_Patient.My_Doctor);
>     end if;
> 
> instead.

Would it be better to express this knowledge:

1. As an explicit initialization on the data structure?

2. As a comment in the specification of Sue_Doctor?

3. As a run-time check at the beginning of Sue_Doctor?

I can see #2 or #3. I have a hard time seeing how the maintainer would
think to review the data structure for Some_Patient in order to know
whether or not to do a check for null on My_Doctor. For that matter,
what about:

  Contact_Doctor(Some_Patient.My_Doctor);

Doesn't it have to have the same behavior as Sue_Doctor, for your
coding convention to work? What if Contact_Doctor should contact the
patient if there is no doctor?

> 
> >2. Suppose I change the application slightly, such that a Patient is created
> >when they enter the waiting room. In this case, it may be quite valid for a
> >Patient to exist, but not have an associated Doctor. In order to maintain
> >your coding convention, the maintainer must go back to the Patient record and
> >add an explicit initialization. Is this likely to happen, particularly if the
> >code is separated from the data structure, and nothing will happen functionally
> >if the maintainer fails to do this? Or will your coding convention "drift" over
> >time, causing more confusion?
> 
> If the application is different, then yes, you have to change the code.
> If you used comments, instead, then you'd have to change the comments.

> Either way, there's a danger that the programmer will forget to do it.
> This is the nature of comments, which is the same nature as un-enforced
> coding conventions.  I don't see any way around that, except to outlaw
> both comments and coding conventions.

See my example. You can use Ada to enforce the pointer abstraction, consolidate
comments in one useful location, and still have coding conventions!

> Agreed.  What's the alternative?  Comments?  Same maintenance problem.
> Don't bother?  Well, there is some useful information here, do you
> really want to hide it from the users of this type?

Third alternative: Abstraction.

> I guess we'll have to agree to disagree.  If you work on *my* project,
> you'll have to obey *my* conventions.  If I work on *your* project I
> will, of course, obey your conventions.

Again, what happens when your code hits my project (or the other way
around)? Wouldn't it be better if the code were readily maintained
without reference to coding conventions?

What happens if you decide to change your coding conventions? Aren't you
restricted, since some of your code may no longer make sense if you change
a convention?

> Either way, the maintainer can
> benefit from knowing what the coding conventions are.

True, but you go further. It is _required_ to know the coding conventions
to understand what your code is trying to tell the maintainer. I would
think that this should not be required.

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-13  0:00                 ` Ken Garlington
@ 1996-05-13  0:00                   ` Robert A Duff
  1996-05-13  0:00                     ` Ken Garlington
  1996-05-13  0:00                   ` Ken Garlington
  1 sibling, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-13  0:00 UTC (permalink / raw)



In article <3196FBD9.6AA1@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>Robert A Duff wrote:
>> OK, this explains our disagreement.  I would *not* be happy with such a
>> language.  You would, and would rely on default-initialization to
>> T'First.  IMHO, that just masks real bugs in the program.
>
>The part that perplexes me about this statement (other than not being able
>to figure out what kind of "bugs" are "masked"), is that it only seems to
>apply to the implicit action that occurs when an access type is declared.
>For example, do you also avoid the following constructs? If not, why not?
>
>1. Use of "others" in an aggregate expression, case statement, etc.

I don't use "others" very often.  "Others" means, to me, that I mean
to include all "other" cases, including the ones that haven't been
invented yet.  Sometimes that makes sense.

>2. Controlled types
>
>3. Dispatching
>
>4. Use of default values for components of a record type.

No, I don't avoid those.

>If you are consistent in your approach, then I can at least agree that
>you are expressing a philosophy regarding the use of the language ("no
>implicit actions, since they hide bugs." If you're not consistent,
>then I don't understand why the philosophy makes sense in some cases,
>but not others.

I'm not complaining about implicitness in general.  When I write
"others", or when I write a default value for a record component, I'm
the one writing the program and I know what I'm doing.  In the
implicit null case, Jean Ichbiah decided in 1980 that my pointers
should be initialized to null, and of course he had no idea what my
program needs today.  My point is that the programmer, not the
language designer, should be in charge of deciding whether a default
value is a good idea, and if so, what that default value should be.
The language should be in charge of detecting uninit vars.

Note that I'm not saying you should always initialize to 'null'.  I'm
perfectly happy to see "X: Some_Pointer := new Something'(...);" or
"X: Some_Pointer := Some_Other_Pointer;".

>However, I think it's "philosophy" that's backed up by experience in
>maintaining code. Have you done experiments that showed the value of adding
>extra code in this case?

No, I have no experimental evidence.

>> Let me be quite clear about what I would *like* in a language: Any type
>> that's built in to the language, and is treated as a non-composite
>> entity, should have run-time detection of uninit vars.
>
>OK. Why do you feel that you can't achieve this in Ada? With Ada 95, you
>have the ability to detect uninitialized scalars at any point in the program,
>so long as the compiler can generate a "marker" value for the object (and
>it supports the Safety and Security annex, of course).

Because (1) it only works for types don't need extra bits, and (2) not
every reference is detected.

>However, to do this consistently in Ada, you need to make a data abstraction
>for each type for which you want such protection. Just providing a redundant
>initialization fails to meet your goal.

Agreed.  But I'm not going to invent a new abstraction for indexing
Strings.  I'm going to use type Integer.

>And you stated for another case of "consistency":
>
>> Seems like a bogus argument, to me.
>> I said integers and pointers should behave the same with respect to
>> initialization.  Composites should not.
>
>Why not? Usually, consistency = simplicity, right?
>
>Note also that "consistency" in one dimension (making access values look like
>integers) can cause inconsistency in another dimension (see my discussion of
>extending your philosophy to other areas of the coding standards).

I never said "integers are exactly like access types in every
respect".  So your argument is bogus.  I just said they should be
alike with respect to detection of uninitialized vars.  (I hope we
both agree that it's good that arithmetic is available for integers,
but not for access types.)

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-13  0:00                 ` Ken Garlington
  1996-05-13  0:00                   ` Robert A Duff
@ 1996-05-13  0:00                   ` Ken Garlington
  1996-05-13  0:00                     ` Robert A Duff
  1 sibling, 1 reply; 52+ messages in thread
From: Ken Garlington @ 1996-05-13  0:00 UTC (permalink / raw)



One correction to an earlier statement I made (which actually has very little
to do with explicit initialization to null, but anyway...)

I said that you should be able to use 'Size to ensure that an invalid
representation of a scalar exists. AI95-00051 correctly points out that
there will be at least two cases where this shouldn't happen:

1. With an aliased object. (However, you should be able to use 'Size on
the type of that object, at least in some cases).

2. With an object that already takes up all the size available for that
architecture (e.g., an integer from System.Min_Int to System.Max_Int).

(I don't exactly buy the fixed/floating point restriction, BTW, and I sent
in a comment on that).

So, what do you do then? Well, it seems to me that you could have, in the
body of the abstraction, a check to see if an invalid value is possible.
At least, you can check integers and enumeration values against System.Max_Int;
I'm guessing there's a more general case that would handle modular types,
and maybe the other cases,too. Anyway, assuming you could devise such a test,
you could have that test done during the elaboration of the type abstraction
and cause Program_Error to be raised. Then, you could have a second generic
that would be used in those cases, which would have the type wrapped in a record
that also contained a boolean called "Unset". The code in the ADT could initialize
this to True, and then set it to False any time a Set operation was called.
That's a little slow, but you would only need it for those types that can't have
their own built-in out-of-range value, which should be a fairly small subset of
the types in an average program.

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-13  0:00                   ` Ken Garlington
@ 1996-05-13  0:00                     ` Robert A Duff
  1996-05-13  0:00                       ` Ken Garlington
  0 siblings, 1 reply; 52+ messages in thread
From: Robert A Duff @ 1996-05-13  0:00 UTC (permalink / raw)



In article <31975399.6F2C@lmtas.lmco.com>,
Ken Garlington  <garlingtonke@lmtas.lmco.com> wrote:
>I said that you should be able to use 'Size to ensure that an invalid
>representation of a scalar exists. AI95-00051 correctly points out that
>there will be at least two cases where this shouldn't happen:

But note that AI-51 has not yet been approved by the ARG.  This AI was
considered at the November meeting, and I rewrote it to reflect my
understanding of the concensus.  It will be considered again at the June
meeting, but the current (draft) version has no official status -- it's
just my personal opinion.

>So, what do you do then? Well, it seems to me that you could have, in
>the body of the abstraction, a check to see if an invalid value is
>possible.  At least, you can check integers and enumeration values
>against System.Max_Int; I'm guessing there's a more general case that
>would handle modular types, and maybe the other cases,too. Anyway,
>assuming you could devise such a test, you could have that test done
>during the elaboration of the type abstraction and cause Program_Error
>to be raised. Then, you could have a second generic that would be used
>in those cases, which would have the type wrapped in a record that also
>contained a boolean called "Unset". The code in the ADT could
>initialize this to True, and then set it to False any time a Set
>operation was called.  That's a little slow, but you would only need it
>for those types that can't have their own built-in out-of-range value,
>which should be a fairly small subset of the types in an average
>program.

Well, type Character uses all the bits up on most implementations, and
(in a packed array) Boolean uses up all the bits (namely 1 bit).  Type
Integer uses up all the bits on most implementations.  These aren't
things that you normally encapsulate in an "abstraction".

- Bob




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-13  0:00                     ` Robert A Duff
@ 1996-05-13  0:00                       ` Ken Garlington
  0 siblings, 0 replies; 52+ messages in thread
From: Ken Garlington @ 1996-05-13  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> But note that AI-51 has not yet been approved by the ARG.  This AI was
> considered at the November meeting, and I rewrote it to reflect my
> understanding of the concensus.  It will be considered again at the June
> meeting, but the current (draft) version has no official status -- it's
> just my personal opinion.

Good point. However, for at least the two cases I agree with, I can't see
the alternative, so I'm willing to accept that those will be approved.

> Well, type Character uses all the bits up on most implementations, and
> (in a packed array) Boolean uses up all the bits (namely 1 bit).  Type
> Integer uses up all the bits on most implementations.  These aren't
> things that you normally encapsulate in an "abstraction".

Well, I (and Brian Wichmann) might disagree.

More importantly, keep in mind that you want to extend the capabilities
of the language. You should expect that you will have to do some things
that "normally" aren't done. The real questions are:

1. Does the language give you the building blocks to do what you want,
with reasonable efficiency and reliability?

I think the answer here is "yes," based on my experience with GADTs and ADTs.

2. Does the language let you readily assemble these building blocks, and use
them effectively?

I'll accept that not everyone likes using a lot of ADTs.
However, I think that the power of generics
(and maybe controlled types?) certainly makes it easier to add uninit checks
to Ada than to applications coded in most other languages.

Nonetheless, you should agree that, if we limit the use of this technique
to the class of objects (Pointers) we were discussing initially, a GADT
is going to be much more readable, reliable, and modifiable (e.g converting between
accesses and integers) than your coding standard, and with almost no
additional source code. In fact, if your coding standard requires the declaration
of No_Particular... for each type, the number of source lines may be _less_
(assuming you count each instantiation as one source line vs. two source lines
for each access type using your coding standard).

So, I'm still back to the core issue. Why do the explicit intialization to
the literal "null", when there's much more effective approaches to solving your
pointer problems for almost no additional cost (and maybe less cost)?

> 
> - Bob

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: To Initialise or not
  1996-05-13  0:00                   ` Robert A Duff
@ 1996-05-13  0:00                     ` Ken Garlington
  0 siblings, 0 replies; 52+ messages in thread
From: Ken Garlington @ 1996-05-13  0:00 UTC (permalink / raw)



Robert A Duff wrote:
> 
> >1. Use of "others" in an aggregate expression, case statement, etc.
> 
> I don't use "others" very often.  "Others" means, to me, that I mean
> to include all "other" cases, including the ones that haven't been
> invented yet.  Sometimes that makes sense.

So, if you needed to set a long string to all blanks, you
wouldn't use (others => ' ') unless you expected that the size of the
string would change?

> I'm not complaining about implicitness in general.  When I write
> "others", or when I write a default value for a record component, I'm
> the one writing the program and I know what I'm doing.  In the
> implicit null case, Jean Ichbiah decided in 1980 that my pointers
> should be initialized to null, and of course he had no idea what my
> program needs today.

Well, he presumed that you wanted a deference of a pointer to always 
be detected, which certainly seems like a good guess, given the history of
pointers used in other languages.

> My point is that the programmer, not the
> language designer, should be in charge of deciding whether a default
> value is a good idea, and if so, what that default value should be.
> The language should be in charge of detecting uninit vars.

Or, alternately, providing the building blocks for you to effectively
extend the language (which I believe Normalize_Scalars and 'Valid lets
you do, when used with an ADT or GADT).

Which things should be "hard-wired" in the language and which should not
is always going to be a philosophy issue. If your real complaint is that
you would rather have a pragma Normalize_Accesses instead of the default
initialization to null, that's OK I guess (although I don't know how that
would work, since you can't really talk about the range of an access value
in Ada 95). I still don't see how your coding convention helps this issue
in any meaningful way, however.

> Note that I'm not saying you should always initialize to 'null'.  I'm
> perfectly happy to see "X: Some_Pointer := new Something'(...);" or
> "X: Some_Pointer := Some_Other_Pointer;".

Right - there's nothing wrong with those other initializations. The intent
of the initialization is (or should be) clear in each case. It's just the
literal "null" that I find wanting.

> 
> >However, I think it's "philosophy" that's backed up by experience in
> >maintaining code. Have you done experiments that showed the value of adding
> >extra code in this case?
> 
> No, I have no experimental evidence.
> 
> >> Let me be quite clear about what I would *like* in a language: Any type
> >> that's built in to the language, and is treated as a non-composite
> >> entity, should have run-time detection of uninit vars.
> >
> >OK. Why do you feel that you can't achieve this in Ada? With Ada 95, you
> >have the ability to detect uninitialized scalars at any point in the program,
> >so long as the compiler can generate a "marker" value for the object (and
> >it supports the Safety and Security annex, of course).
> 
> Because (1) it only works for types don't need extra bits, and (2) not
> every reference is detected.

Well, assuming that my condition holds (a marker value is possible), and
you use an abstraction for the reads to the type, it should detect every
reference via the abstraction.

Actually, even if a direct marker isn't possible, you can create an
auxiliary marker (a boolean bundled with the "real" type) in the abstraction.

> >However, to do this consistently in Ada, you need to make a data abstraction
> >for each type for which you want such protection. Just providing a redundant
> >initialization fails to meet your goal.
> 
> Agreed.  But I'm not going to invent a new abstraction for indexing
> Strings.  I'm going to use type Integer.

That's your call, of course. However, I don't see the alternative to get
what you want reliably (Normalize_Scalars will still detect an error,
occasionally, of course).

I'm still mystified how initialization to null helps you here...

> I never said "integers are exactly like access types in every
> respect".  So your argument is bogus.  I just said they should be
> alike with respect to detection of uninitialized vars.

My argument was only with respect to initialization -- which is exactly
where you said your argument resided (your previous post mentioned
explicit initialization rules, not detection of uninit vars). All I said was, 
"why not treat integers and aggregates alike with respect to initialization,
just as you want to treat integers and accesses alike with respect to 
initialization?"

Seems perfectly "consistent" for me.

> (I hope we
> both agree that it's good that arithmetic is available for integers,
> but not for access types.)

Actually, when writing OS code in Ada 83, I have had need to do unchecked
conversions to convert access types to integers so that I could do
simple arithmetic on them. I certainly agree that you shouldn't provide
such operations by default, however.

Aren't you making my point? Access types are fundamentally different from
integers, so why should the initialization rules be alike?

Of course, in Ada 95, I _can_ do arithmetic on an access type _portably_.
Ain't Ada grand?

> 
> - Bob

-- 
LMTAS - "Our Brand Means Quality"




^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~1996-05-13  0:00 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1996-04-29  0:00 To Initialise or not Steve O'Neill
1996-04-29  0:00 ` Ken Garlington
1996-04-29  0:00   ` Robert Dewar
1996-04-30  0:00     ` Ken Garlington
1996-04-30  0:00   ` Robert A Duff
1996-04-30  0:00     ` Ken Garlington
1996-04-30  0:00       ` Robert A Duff
1996-05-01  0:00         ` Keith Thompson
1996-05-01  0:00           ` Robert A Duff
1996-05-02  0:00             ` Keith Thompson
1996-05-03  0:00               ` Robert A Duff
1996-05-01  0:00           ` Theodore E. Dennison
1996-05-01  0:00             ` Robert A Duff
1996-05-02  0:00               ` Theodore E. Dennison
1996-05-02  0:00                 ` Chris Warack <sys mgr>
1996-05-02  0:00                   ` Robert A Duff
1996-05-06  0:00                   ` Ken Garlington
1996-05-02  0:00                 ` Robert A Duff
1996-05-02  0:00               ` Michael F Brenner
1996-05-02  0:00                 ` Robert A Duff
1996-05-04  0:00                   ` Kevin D. Heatwole
1996-05-06  0:00               ` Ken Garlington
1996-05-07  0:00                 ` Robert A Duff
1996-05-08  0:00                   ` Ken Garlington
1996-05-08  0:00                     ` Robert A Duff
1996-05-09  0:00                       ` Ken Garlington
1996-05-09  0:00                         ` Robert A Duff
1996-05-01  0:00             ` Dale Stanbrough
1996-05-02  0:00             ` Robert Dewar
1996-05-02  0:00               ` Robert A Duff
1996-05-02  0:00               ` Theodore E. Dennison
1996-05-06  0:00           ` Ken Garlington
1996-05-06  0:00         ` Ken Garlington
1996-05-07  0:00           ` Robert A Duff
1996-05-08  0:00             ` Ken Garlington
1996-05-09  0:00               ` Robert A Duff
1996-05-10  0:00                 ` Robert A Duff
1996-05-10  0:00                   ` Ken Garlington
1996-05-10  0:00                     ` Robert A Duff
1996-05-10  0:00                       ` Ken Garlington
1996-05-11  0:00                         ` Robert A Duff
1996-05-11  0:00                         ` David Kristola
1996-05-11  0:00                           ` Robert A Duff
1996-05-13  0:00                 ` Ken Garlington
1996-05-13  0:00                   ` Robert A Duff
1996-05-13  0:00                     ` Ken Garlington
1996-05-13  0:00                   ` Ken Garlington
1996-05-13  0:00                     ` Robert A Duff
1996-05-13  0:00                       ` Ken Garlington
1996-04-30  0:00     ` Robert A Duff
1996-05-01  0:00     ` Patrick Richard Wibbeler
1996-05-06  0:00       ` Ken Garlington

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox