Redefine "=" and still use the old one? 
Author Message
 Redefine "=" and still use the old one?

I have a funny problem with limited types and "=" redefinition.
Let's assume I want to define an abstract data type in this way:

package ABSTRACT_DATA_TYPE is
        type LIST is limited private;
        -- Subprograms, etc
        function "=" (L, R : LIST) return boolean;
private
        type NODE;
        type LIST is access NODE;
end ABSTRACT_DATA_TYPE;

Simple and straightforward, huh? So, I want LIST to be actually an access type.
Now, how can I program the "=" function?

package body ABSTRACT_DATA_TYPE is

        type NODE is record
                Value : integer;
                Next : LIST;
        end record;

        function "=" (L, R : LIST) return boolean is
                L_iterator : LIST := L;
                R_iterator : LIST := R;
        begin
                while L_iterator /= null and R_iterator /= null loop
                        if (L_iterator = null or R_iterator = null) or else
                            L_iterator.all /= R_iterator.all then
                                return false;
                        else
                                L_iterator := L_iterator.Next;
                                R_iterator := R_iterator.Next;
                        end if;
                end loop;
                return true;
        end "=";

end ABSTRACT_DATA_TYPE;

The "=" between L_iterator and null will be the new "=" defined in the spec',
not the original one! The above code will run 'til STORAGE_ERROR stops it.

How can I use the original "="? I've found two solutions, both inelegant (IMHO).

First one, with a record:
...
        type NODE;
        type ACCESS_LIST is access NODE;
        -- Use a record to encapsulate the "comparable" type
        type LIST is record
                Value : ACCESS_LIST;
        end record;
...
(Same function, but with L_iterator.Value instead of L_iterator)

Second one, much funnier, with generics:

(in the body)
        -- Use a generic function to save the old "=".
        generic
                type ITEM is private;
        function GENERIC_EQUALS (L, R : ITEM) return boolean;
        function GENERIC_EQUALS (L, R : ITEM) return boolean is
        begin return L=R; end GENERIC_EQUALS;
        function EQUALS is new GENERIC_EQUALS (LIST);
...
(Now, use EQUALS.)

It's strange that I've found nothing about this problem in books. Some authors
don't use limited types (and therefore don't redefine "="), some don't call it
"=", but EQUAL (a la Booch), others ignore the problem. I was amazed to see that
I'm not able to write such a simple code.

Is there a better way?

Stephane Bortzmeyer           Conservatoire National des Arts et Metiers        

                              292, rue Saint-Martin                    
tel: +33 (1) 40 27 27 31      75141 Paris Cedex 03
fax: +33 (1) 40 27 27 72      France    

"C'est la nuit qu'il est beau de croire a la lumiere." E. Rostand



Sat, 06 Aug 1994 22:21:54 GMT  
 Redefine "=" and still use the old one?


due to a lack of time at this moment. I apologize and modify details :

: It is always a bad idea to have the type to implement and the type
: which actually implements being the same. If one looks at the abstract
: data type theory, then there is always an implementation function
: betweem them.
:
: The conceptually best solution is to explicit this function.

This is an important principle, and no mistake is in it.

: In Ada words, the solution is to derive the first type (the complete
: definition of the private type) to define the second (the internal
: implementation), or alternatively repeat the definition :

It is only one solution of course, and Stephane's solution with records
is perfectly valid with respect to the principle.

: private
:    type NODE ;
:    type LIST is access NODE;
:    type IMPLEMENT_LIST is access NODE;
: end ;        

Here I was mistaking in the details. Of course, the general solution must
declare the implementing type BEFORE the other, and the complete definition
of the private type must be a derivation. Solutions with less constraints
apply to numeric types, not to other types, due to specific features of
conversions.

We are here in a case where no special difficulty arise due to Ada, but
where Ada helps to recognize conceptual unknown difficulties.

---------------------------------------------------------------------
Michel GAUTHIER / invited professor
                  L. G. L.    (Software Engineering Laboratory)
                  E. P. F. L. (Federal Swiss Institute of Technology)
                  CH-1015 Lausanne
[-------------------------------------------------------------------]
[ inheritance is surely a good answer, but who knows the question ? ]
[-------------------------------------------------------------------]



Mon, 08 Aug 1994 15:35:15 GMT  
 Redefine "=" and still use the old one?

 >  I have a funny problem with limited types and "=" redefinition.
 >  Let's assume I want to define an abstract data type in this way:
 >  [...]
 >  Now, how can I program the "=" function?

   Included are two additional solutions to this problem, and some
comments about this style of programming and Ada 9X. First, however,
there is another bug to fix in the program.  It should compare
L_Iterator.Value to R_Iterator.Value not L_Iterator.all to
R_Iterator.all.

   Now, my solution to this type of hiding has always been to derive
LIST from a parent type, and then use type conversions in the body to
access the hidden operations:

   package ABSTRACT_DATA_TYPE is
           type LIST is limited private;
           -- Subprograms, etc
           function "=" (L, R : LIST) return boolean;
   private
           type NODE;
           type ACTUAL_LIST is access NODE;
           type LIST is new NODE;
   end ABSTRACT_DATA_TYPE;

-- Now in the body:

   package body ABSTRACT_DATA_TYPE is

           type NODE is record
                   Value : integer;
                   Next : LIST;
           end record;

           function "=" (L, R : LIST) return boolean is
                   L_iterator : LIST := ACTUAL_LIST(L);
                   R_iterator : LIST := ACTUAL_LIST(R);
           begin
                   while L_iterator /= null and R_iterator /= null loop
                           if (L_iterator = null or R_iterator = null) or else
                               L_iterator.Value /= R_iterator.Value then
                                   return false;
                           else
                                   L_iterator := L_iterator.Next;
                                   R_iterator := R_iterator.Next;
                           end if;
                   end loop;
                   return true;
           end "=";

   end ABSTRACT_DATA_TYPE;
   -- all done.

   --     The problem with this approach is that where both "="
   -- operations are visible (in private part and the body of the
   -- package), it is up to the programmer to make sure that the
   -- overload resolution gives the "right" function at that point.
   -- The problem more often is figuring out which one you want to
   -- use, than figuring out which one you get.  However, generics can
   -- make things dicey.

 >  Second one, much funnier, with generics:

   --     A much more elegant generic solution is to provide the body
   -- of "=" via a generic instantiation. In this sort of small
   -- example, it makes for a lot more work, but often you have many
   -- exported operations to write and they share the generic
   -- overhead:

   package ABSTRACT_DATA_TYPE is
           type LIST is limited private;
           -- Subprograms, etc
           function "=" (L, R : LIST) return boolean;
   private
           type NODE;
           type LIST is access NODE;
   end ABSTRACT_DATA_TYPE;
   -- original spec

   package body ABSTRACT_DATA_TYPE is

     type NODE is record
        Value : integer;
        Next : LIST;
     end record;

     generic
        type ENTRY is private;
        type LIST is access ENTRY;
        with function NEXT(L: LIST) return LIST is <>;
        with function COMPARE_ELEMENTS(L, R: ENTRY) return BOOLEAN is <>;
     function EQUAL(L, R: LIST) return BOOLEAN;

     function EQUAL(L, R : LIST) return BOOLEAN is
        L_iterator : LIST := L;
        R_iterator : LIST := R;
     begin
        while L_iterator /= null and R_iterator /= null loop
           if (L_iterator = null or R_iterator = null) or else
               not COMPARE_ELEMENTS(L_iterator, R_iterator)
           then return FALSE;
           else
              L_iterator := NEXT(L_iterator);
              R_iterator := NEXT(R_iterator);
           end if;
        end loop;
        return TRUE;
     end "=";

   -- Now we need to create the two function parameters:

   function NEXT(L: LIST) return LIST is
   begin return L.NEXT; end NEXT;

   function COMPARE_ELEMENTS(L, R: ENTRY) return BOOLEAN is
   begin return L.Value = R.Value; end COMPARE_ELEMENTS;

   -- now it's time to instantiate:

   function EQ is new EQUAL(NODE, LIST);

   -- and now the body of "=":

   function "=" (L,R: LIST) return BOOLEAN is
   begin return EQ(L,R); end "="

   -- Whew!

   end ABSTRACT_DATA_TYPE;

    Now for the comments on Ada 9X: The plethora of small "wrapper"
functions like NEXT, COMPARE_ELEMENTS, and the body of "=" above is
one of the things that makes doing OOP in Ada 83 so difficult.  The
current draft of the mapping document (version 4) contains a number of
features to minimize the number of such wrappers required, but some
are on the endangered species list in the "zero-based bugeting"
proposal.  The above simple example shows why these fixes are so
necessary for OOP in Ada to succeed.  One additional--potentially
overloaded--function name is not a problem.  Hundreds of them makes
understanding what is going on a nightmare, especially in cases where
you are writing the bodies for some of the exported operators.
--

                                        Robert I. Eachus

with STANDARD_DISCLAIMER;
use  STANDARD_DISCLAIMER;
function MESSAGE (TEXT: in CLEVER_IDEAS) return BETTER_IDEAS is...



Tue, 09 Aug 1994 22:56:02 GMT  
 
 [ 3 post ] 

 Relevant Pages 

1. Using "redefines" in working storage section

2. "Fastest" one-on-one file update

3. Pb with use of redefined "=" operator

4. Redefining "\n"

5. "redefine keys"

6. SUMMARY: Redefine "=" and still use the old one?

7. string.join(["Tk 4.2p2", "Python 1.4", "Win32", "free"], "for")

8. Hi, this code: text0 = "One $BLAH Three" text1 = "One @BLAH Three" text0.sub!("$BLAH", "Two") text1.sub!("@BLAH", "Two") print text0,"\n" print text1,"\n" produces thiHi, this code: text0 = "One $BLAH Three" text1 = "One @BLAH Three" text0.sub!("$BLAH", "T

9. That old "Create Directory" question again

10. "Old fashioned" menu-behavior

11. Looking for "old " VO2

12. New "old" logo version UCBLogo 4.61

 

 
Powered by phpBB® Forum Software