comp.lang.ada
 help / color / mirror / Atom feed
From: matthew_heaney@acm.org (Matthew Heaney)
Subject: Re: Renaming Fixed Point Mutiplicative operator in Ada 95
Date: 1998/05/21
Date: 1998-05-21T00:00:00+00:00	[thread overview]
Message-ID: <matthew_heaney-ya023680002105980952170001@news.ni.net> (raw)
In-Reply-To: 01bd84c3$47215d60$440029a1@m00rq900


In article <01bd84c3$47215d60$440029a1@m00rq900>, "Stuart Hutchesson"
<shutch@innotts.co.uk> wrote:

(start of quote)
Multiplication was just an example of the compilation problem  - division
is the REAL killer - how do you cope with divide-by-zero exceptions in
critical system when all you are allowed is the catch-all exception
handler, (and run-time checks switched off)? Terminating with an exception
is not an option when you are 2 miles above the Atlantic!  I would prefer
to know EXACTLY what is going on in as much of the object code produced
than leave it to the whim of the compiler.
(end of quote)

I would have handled it at a higher layer of abstraction - not by
overriding the predefined arithematic operators of a scalar type.  For
example,

type Target is limited private;

procedure Move (T : in out Target) is
begin
    <do some math using fixed point types>
exception
   when Constraint_Error =>
      <handle error>
end Move;

As you state, this may not be a solution for you, as you're trying to
repair legacy code.

(start of quote)
(BTW - as Robert Dewar said - you can override Integer maths..... why not
fixed point?)
(end of quote)

Yes, this is a non-orthogonal feature of the language.  However, I wouldn't
override the operators for an integer type either, and so the
non-orthogonality never appears (to me).

Maybe overriding the predefined operators for a scalar type would solve
some problems, but that doesn't mean more won't appear.  Consider this:

generic
   type Integer_Type is range <>;
package GP is ...;

package GP is

   procedure Op (...) is
       O : Integer_Type;
   begin
...
       O := O + 10;
...


type My_Integer is range ...;

function Predefined_Add (L, R : My_Integer) 
  return My_Integer renames "+";

function "+" (L, R : My_Integer)
   return My_Integer;

package P is new GP (My_Integer);

Now, which addition operation do you think package P uses?

If you think it's going to use your special overridden version, then think
again.  Predefined operators reemerge if they're not explicitly imported. 
So you could fix this by doing

generic
   type Integer_Type is range <>;
   with function "+" (L, R : Integer_Type) return Integer_Type is <>;
package GP ...

And all is well until some maintenance programmer does this

package body GP is
   
   procedure Op (...) is
      O : Integer_Type;
   begin
      ... O := O - 5;

and forgot to import the subtraction operator.  If the type you pass in has
overridden subtraction, then what does Op do exactly?  Who knows?  I am
unable to mentally reason about behavior of this code, because I don't even
know what subtraction means anymore.

The reemergence of predefined operators should strike fear into the hearts
of men.  When I look at an addition or a subtraction, I have to do mental
gymnastics to determine whether this is the correct operator.  Or try to
determine whether the generic will continue to work, if it's using the
predefined operator instead of my overriding one.

So, why not save me the trouble, and not override the predefined operators
of a scalar type?  It should give you great pause, any time you're thinking
of replacing an predefined operator.  How many places in the code is the
type passed to a generic?  Does that generic use the operator, and import
it as a generic formal operation?

Equality is even more pernicious.  Suppose I have a stack, say

generic
   type Stack_Item is private;
   Max_Depth : Positive;
package Stacks is

   type Bounded_Stack is private;

   function "=" (L, R : Bounded_Stack) return Boolean;
...
private

   type Stack_Item_Array is 
      array (Positive range <>) of Stack_Item;

   type Bounded_Stack is
       record
          Items : Stack_Item_Array (1 .. Max_Depth);
          Depth : Natural := 0;
       end record;

end Stacks;

package body Stacks is

   function "=" (L, R : Bounded_Stack) is
   begin
      return L.Items (1 .. L.Depth) = R.Items (1 .. R.Depth);
   end;
...


This looks OK, doesn't it?  It compiles and runs fine.  Until I do this

type My_Integer is range ...;
function "=" (L, R : My_Integer) return Boolean;

package My_Integer_Stacks is new Stacks (My_Integer, 10);

Now equality doesn't work anymore!  That's because predefined equality
reemerged as a result of passing the type to the generic.  So let's fix the
stack package, by importing the operator:

generic
   type Stack_Item is private;
   with function "=" (L, R : Stack_Item) return Boolean is <>;
   Max_Depth : Positive;
package Stacks is ...;

Guess what: the package still doesn't work!  Look carefully at the code. 
See anything wrong?

You probably don't see any problems, and that's precisely the issue. 
Predefined equality has reemerged again, in spite of importing it as a
generic formal, when I declared this array

type STack_ITem_Array is ... of STack_Item;

So when we do an array comparison, as in 

   return L.Items (1 .. L.Depth) = R.Items (1 .. R.Depth);

this is using the predefined equality for STack_Item, in spite of the fact
that that operator was imported as a generic formal.

The solution is to ditch the array comparison, so do an item-by-item
compare manually:

function "=" (L, R : Stack) return Boolean is
begin
   if L.Depth /= R.Depth then
     return False;
   end if;

   for I in 1 .. L.Depth loop
      if L.Items (I) /= R.Items (I) then
         return False;
      end if;
   end loop;
   
   return True;
end "=";

That's why I like to take away equality when I declare the array:

type Stack_Item_Array is ...;

function "=" (L, R : Stack_Item_Array) return Boolean is abstract;

Any array comparison will then be flagged as illegal at compile-time, to
remind the unwary maintenance programmer who "forgot" that predefined
equality re-emerges.

I'm not saying, "Never override predefined operators of a scalar type." 
I'm just saying, do it after you've exhausted all other solutions.  The Ada
philosophy is that what the code does should be obvious by just looking at
the code.  But if you replace predefined arithematic operators, that truism
doesn't apply, and it complicates mental reasoning about program behavior.

Sooner or later, a predefined operator for a type will re-emerge, if you've
overridden it.  It may be that overriding the operator solves a problem
now, but you're just playing Russian Roulette, because later on your code
is going to break for another reason.  The error won't even be directly
visible.  You just have to _know_.

And how many programmers do?  Perhaps you and your staff right now are
fully aware of the perniciousness of re-emergence.  But can you guarantee
that a future developers will be aware of this "feature" of the language?




  parent reply	other threads:[~1998-05-21  0:00 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-05-19  0:00 Renaming Fixed Point Mutiplicative operator in Ada 95 Stuart Hutchesson
1998-05-19  0:00 ` Matthew Heaney
1998-05-20  0:00   ` Robert Dewar
     [not found]     ` <matthew_heaney-ya023680002005981908570001@news.ni.net>
     [not found]       ` <01bd84c3$47215d60$440029a1@m00rq900>
1998-05-21  0:00         ` Robert Dewar
1998-05-21  0:00           ` Simon Pilgrim
1998-05-21  0:00             ` Matthew Heaney
1998-05-22  0:00               ` Robert I. Eachus
1998-05-22  0:00           ` Rod Chapman
1998-05-22  0:00             ` John McCabe
1998-05-22  0:00           ` Stuart Hutchesson
1998-05-22  0:00             ` Matthew Heaney
1998-05-23  0:00             ` Robert Dewar
1998-05-21  0:00         ` Robert Dewar
1998-05-21  0:00         ` Matthew Heaney [this message]
1998-05-21  0:00       ` Robert Dewar
1998-05-22  0:00         ` Robert I. Eachus
1998-05-23  0:00           ` Robert Dewar
1998-05-21  0:00       ` John McCabe
1998-05-21  0:00         ` Matthew Heaney
1998-05-21  0:00         ` Robert Dewar
replies disabled

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