comp.lang.ada
 help / color / mirror / Atom feed
* Re: Active Iteration (was: How to use abstract data types)
@ 1998-05-13  0:00 adam
  1998-05-13  0:00 ` Matthew Heaney
  0 siblings, 1 reply; 6+ messages in thread
From: adam @ 1998-05-13  0:00 UTC (permalink / raw)



Matthew, thanks for all the information.  It helps to be exposed to a
number of different approaches, so once I understand them all, I can
pick which one seems best.

A question about one thing you wrote:

> The dynamic approach is a bit more flexible, but it could be made to work
> (but see below).  Notice that you've already made a static commitment in
> your case statement (which is by definition static).  The
> Let_Client_Play_With_Library could have been made into a generic, and
> instantiated once per library (ie once per brach of the case statement).
> Something like:

> procedure Let_Client_Play_With_Test_Lib is
>    new Let_Client_Play (Test_Library...);
>
> procedure Let_Client_Play_With_Another_Test_Lib is
>    new Let_Client_Play (Another_Test_Library...);

Regarding the example at LRM 3.9.3(15-16):

    package Sets is
        subtype Element_Type is Natural;
        type Set is abstract tagged null record;
        function Empty return Set is abstract;
        function Union(Left, Right : Set) return Set is abstract;
        function Intersection(Left, Right : Set) return Set is abstract;
        function Unit_Set(Element : Element_Type) return Set is abstract;
        procedure Take(Element : out Element_Type; From : in out Set)
             is abstract;
    end Sets;

    Notes on the example: Given the above abstract type, one could
    then derive various (nonabstract) extensions of the type,
    representing alternative implementations of a set.  One might use
    a bit vector, but impose an upper bound on the largest element
    representable, while another might use a hash table, trading off
    space for flexibility.

It seems to me that, if your two choices are a bit vector and a hash
table, it's likely that other modules in the program that use a Set
would make this choice statically, because they'd already have an idea
of the upper bound on elements in the set.  My question: Would you
therefore choose instead to use a generic to implement a "Set"?  If
not, but if you would choose to use a generic to implement a Library
in my example, what's is the difference between the two examples that
would cause you to use different language constructs to implement
them?

                                -- thanks, Adam

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/   Now offering spam-free web-based newsreading




^ permalink raw reply	[flat|nested] 6+ messages in thread
* Re: Active Iteration (was: How to use abstract data types)
@ 1998-05-08  0:00 adam
  1998-05-09  0:00 ` Matthew Heaney
  0 siblings, 1 reply; 6+ messages in thread
From: adam @ 1998-05-08  0:00 UTC (permalink / raw)



Matthew, thanks for all the information---this really does help me see
what's going on.

> Use static binding when you need static binding, and use dynamic binding
> when you need dynamic binding.  Don't think that importing a formal derived
> type is somehow "superior" to the traditional way.  It's its complement,
> not replacement.

FWIW, I believe I do need dynamic binding in the tool I'm working on.
Of course, I'm not actually working on a program to list book titles,
but I am working on a program that will have to deal with some
radically different types of files, and the program will either use a
command-line option or take a peek at a file's header to figure out
what kind of file it's dealing with.  Sorry that I didn't make that
clear when I came up with my "Libraries" example; I didn't see at the
time that it was necessary to mention this.

So if, say, the program asks the user to select a type of library from
a menu, and then contains code like

      if Menu_Option = 1 then
          declare
              Lib : aliased Libraries.Test.Library :=
                        Libraries.Test.The_Library;
          begin
              Let_The_User_Play_With_The_Library (Lib'access);
          end;
      elsif Menu_Option = 2 then
          declare
              Lib : aliased Libraries.Another_Test.Library :=
                        Libraries.Another_Test.The_Library;
          begin
              Let_The_User_Play_With_The_Library (Lib'access);
          end;
      elsif Menu_Option = 3 then
          declare
              Lib : aliased Libraries.Amazon_Dot_Com.Library :=
                        Libraries.Amazon_Dot_Com.The_Library;
          begin
              .... some stuff to set up an FTP connection ....
              Let_The_User_Play_With_The_Library (Lib'access);
          end;
      . . .

where Let_The_User_Play_With_The_Library's parameter is "access
Root_Library'class", and Let_The_User_Play_With_The_Library somewhere
down the road calls List_Books, it doesn't seem to me that the generic
version you used in your previous example:

    procedure Library_Package.Test.List_Books is
      new Library_Package.List.List_Books
      (Source_Library  => Test_Library'Class,
       Source_Iterator => Test_Iterator);


    with Library_Package.Test.List_Books;
    use Library_Package.Test.List_Books;

    procedure Libtest2 is
        The_Lib : Library_Package.Test.Test_Library;
    begin
        List_Books (The_Lib, "m");
    end Libtest2;

will work.  However, the example code you included in your more recent
example should work fine in a case like this.  Am I understanding
what's going on correctly?


> 4) Constructors are functions in Ada.  That means to create a new iterator
> object on the heap, you don't return it as a procedure out param.  It
> should be a function that returns the type, ie
>
>    function New_Iterator
>      (Lib : access Root_Library) return Iterator_Access is abstract;
>
>    function New_Iterator
>       (Lib : access Test_Library) return Iterator_Access;

In my original post, my procedure declaration looked like this:

    procedure Scan_By_Title (Lib            : in Library;
                             Reg_Expression : in String;
                             Scan           : out Scan_Info;
                             RE_Error       : out Boolean)
        is abstract;

where Scan_Info is the iterator.  This can't be made into a function
because it returns two things.

But I think I see what happened here.  I was assuming that the
function (or procedure) that constructed the iterator was also the one
that set it up for the specific purpose I needed it for.  (Which might
be right in some cases.)  Your example, though, has one function to
create the iterator, and a second procedure (Scan_For_Substring) to
actually start the scan process.

OK, that makes sense.  Actually, that's the kind of thing I was
looking for with my original post.  I presume that New_Iterator could
create the iterator with some sort of "Useless" flag so that if you
tried Get_Book or Is_Done or Advance on the iterator without one of
the other setup routines, the program would get upset.


> This is an example of an indefinate type - Root_Iterator'Class (remember,
> all classwide types are indefinate) - that is also limited.  For an
> indefinate type, I need to give it some initial value, so I need to invoke
> a constructor to do that.  But I can't for limited types.  Major league
> bummer.

Last time I saw a problem similar to this, it was in a little book by
Joseph Heller . . .


> 8) Don't have a primitive operation of a tagged type return a
> specific type, ie
>
>    function The_Library return Root_Library is abstract;

That was my mistake.  I should have had it return Root_Library'Class.

                                -- Adam

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/   Now offering spam-free web-based newsreading




^ permalink raw reply	[flat|nested] 6+ messages in thread
* Active Iteration (was: How to use abstract data types)
@ 1998-05-05  0:00 Matthew Heaney
  0 siblings, 0 replies; 6+ messages in thread
From: Matthew Heaney @ 1998-05-05  0:00 UTC (permalink / raw)



Here's the idea I had for the library iterator.  The Library_Package.Test
package exports an active iterator that I was hoping to use to iterate over
a Test_Library.  

However, I can't get this code to compile!  My compiler is telling me the
generic actual types don't match the formal discriminant types.

If your compiler can compile this, let me know and I'll report a bug to
ACT.  If this is simply an illegal Ada program, then maybe someone can
figure out an alternative solution.

The idea was to make the List_Books operation a generic procedure that
imports an iterator type as a generic formal type, to iterate over the
library object passed in.  I do it this way because importing an iterator
as derived from an abstract root iterator type won't work, because formal
derived types cannot have a known discriminant (see RM95 12.5.1 (11), a
bummer of a paragraph).  So I just import everything as a non-tagged type
(even though the actual type may in fact be tagged), and import the ops
formal subprogram parameters ("implementation inheritance").

But I can't get the instantiation of List_Books to compile.  Maybe you'll
have more luck.

Matt



package Library_Package is

    type Library is abstract tagged null record;
    type Book is abstract tagged null record;

    function Title (Bk : Book) return string is abstract;

end Library_Package;


package Library_Package.Test is

    type Test_Library is new Library with private;

    type Test_Book is new Book with private;

    type Test_Library_Acc is access all Test_Library;

    type Test_Iterator (Lib : access Test_Library'Class) is
        limited private;

    function Title (Bk : Test_Book) return string;
    procedure Scan_For_Substring (Iter   : in out Test_Iterator;
                                  Substr : in string);
    function Is_Done (Iter : Test_Iterator) return boolean;
    function Get_Book (Iter : Test_Iterator) return Book'Class;
    procedure Advance (Iter : in out Test_Iterator);

private
    type String_P is access string;
    type Book_Info is record
       Title : String_P;
    end record;

    type Test_Book is new Book with record
        Info : Book_Info;
    end record;

    type Book_Array is array (Natural range <>) of Test_Book;

    type Test_Library is
      new Library with record
         Books : Book_Array (1..6) :=
        ( (Info => (Title => new string' ("A Time To Kill"))),
          (Info => (Title => new string' ("The Firm"))),
          (Info => (Title => new string' ("The Pelican Brief"))),
          (Info => (Title => new string' ("The Client"))),
          (Info => (Title => new string' ("The Rainmaker"))),
          (Info => (Title => new string' ("The Runaway Jury"))) );
      end record;


    type Test_Iterator (Lib : access Test_Library'Class) is
       limited record
         Search_String : String_P;
         Index         : Natural;
       end record;

end Library_Package.Test;


with Ada.Strings.Fixed;
package body Library_Package.Test is

    function Title (Bk : Test_Book) return string is
    begin
        return Bk.Info.Title.all;
    end Title;

    procedure Search (Iter : in out Test_Iterator) is
       use Ada.Strings.Fixed;
       Books : Book_Array renames Iter.Lib.Books;
    begin
       while Iter.Index <= Iter.Lib.Books'last loop
          declare
             Book : Test_Book renames Books (Iter.Index);

             Title : String renames Book.Info.Title.all;

             Search_String : String renames Iter.Search_String.all;
          begin
             exit when Index (Title, Search_String) /= 0;
          end;

          Iter.Index := Iter.Index + 1;

        end loop;
    end Search;

    procedure Scan_For_Substring (Iter   : in out Test_Iterator;
                                  Substr : in string) is
    begin
        Iter.Search_String := new string' (Substr);
        Iter.Index := Iter.Lib.Books'first;
        Search (Iter);
    end Scan_For_Substring;

    function Is_Done (Iter : Test_Iterator) return boolean is
    begin
        return (Iter.Index > Iter.Lib.Books'last);
    end Is_Done;

    function Get_Book (Iter : Test_Iterator) return Book'Class is
    begin
        return Iter.Lib.Books (Iter.Index);
    end Get_Book;

    procedure Advance (Iter : in out Test_Iterator) is
    begin
        Iter.Index := Iter.Index + 1;
        Search (Iter);
    end Advance;

end Library_Package.Test;


package Library_Package.List is

   generic
      type Source_Library (<>) is limited private;

      type Source_Iterator
        (Lib : access Source_Library) is limited private;

      with function Is_Done
        (Iter : Source_Iterator) return Boolean is <>;

      with procedure Advance
        (Iter : in out Source_Iterator) is <>;

      with function Get_Book
        (Iter : Source_Iterator) return Book'Class is <>;

      with procedure Scan_For_Substring 
        (Iter  : in out Source_Iterator;
         Subst : in     String) is <>;
   procedure List_Books 
      (Lib : access Source_Library; Substr : in string);

end Library_Package.List;


with Text_IO;
package body Library_Package.List is

   procedure List_Books 
     (Lib    : access Source_Library; 
      Substr : in string) is

      Iter : Source_Iterator (Lib);
    begin
        Scan_For_Substring (Iter, Substr);

        while not Is_Done (Iter) loop

            declare
                Bk : Book'class := Get_Book (Iter);
            begin
                Text_IO.Put_Line (Title (Bk));
            end;

            Advance (Iter);

        end loop;
    end List_Books;

end Library_Package.List;



with Library_Package.List;

procedure Library_Package.Test.List_Books is
  new Library_Package.List.List_Books
  (Source_Library  => Test_Library'Class,
   Source_Iterator => Test_Iterator);
   

with Library_Package.Test.List_Books;
use Library_Package.Test.List_Books;

procedure Libtest2 is
    The_Lib : Library_Package.Test.Test_Library;
begin
    List_Books (The_Lib, "m");
end Libtest2;




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

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

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-05-13  0:00 Active Iteration (was: How to use abstract data types) adam
1998-05-13  0:00 ` Matthew Heaney
  -- strict thread matches above, loose matches on Subject: below --
1998-05-08  0:00 adam
1998-05-09  0:00 ` Matthew Heaney
1998-05-09  0:00   ` Simon Wright
1998-05-05  0:00 Matthew Heaney

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