comp.lang.ada
 help / color / mirror / Atom feed
From: adam@irvine.com
Subject: Re: How to Use Abstract Data Types
Date: 1998/04/30
Date: 1998-04-30T00:00:00+00:00	[thread overview]
Message-ID: <6ib2o9$4gq$1@nnrp1.dejanews.com> (raw)


Robert Eachus wrote:

>  > Am I missing something trivial here?
>
> -- Yes, but you can be forgiven because it is not trivial in many OO
> -- langauges.  There are cases where you have to use access parameters
> -- and all that to duplicate the normal way of doing iterators in
> -- other languages.  In Ada, you are always better off making
> -- iterators objects, but in Ada 95 the much cleaner way of doing
> -- iterators is to use instances of generic packages as the iterator
> -- objects.  Sounds compilicated, but it isn't.  First, we will
> -- realize that Books and libraries are separate abstractions:
>
> package Books is
>
>    type Book is ...;
>    function Title(B: Book) return String;
>    ...
> end Books;
>
> with Books
> package Libraries is
>
>    type Library is abstract tagged null record;
>
>    -- various abstract operations on Libraries.
>
>    generic
>       type Some_Library is new Library with private;
>       This_Library: in out Some_Library;
>    package Iterator is
>       function More return Boolean;
>       function Next return Book'Class;
>    end Iterator;
>
> private
>    ...
> end Libraries;
>
>
>    The body of Iterator will call (abstract) primitives on Libraries.
> Of course, when Iterator is instantiated, it will be on a non-abstract
> class, so there will be actual code to call.  But that is not your
> worry.

But I'm not sure this is what I want.  Unless there's something else
I'm missing, having an Iterator generic package whose body calls
abstract primitives on Libraries isn't what I want.

Let me try to go into more detail.  My original package spec looked
something like this:

    package Library_Package is
        type Library is abstract tagged null record;
        type Book is abstract tagged null record;
        type Scan_Info is abstract tagged null record;  -- (my iterator)
        procedure Scan_By_Title ... is abstract;
        function More_Books ... is abstract;
        procedure Next_Book ... is abstract;
        procedure Close_Scan ... is abstract;
    end Library_Package;

What I envisioned here is that different packages could deal with
different implementations of the library.  For example, I could
perhaps define something like

    package Amazon_Library_Package is
        type Library is new Library_Package.Library with private;
        type Book is new Library_Package.Book with private;
        etc.
    end Amazon_Library_Package;

where the purpose of this package is to define a "library" as all the
books in amazon.com.  The code for Scan_By_Title, More_Books,
Next_Book, as well as the private parts of the types I've defined,
would be set up specifically to FTP to amazon.com and send it commands
that cause amazon.com to return the information needed to implement
these functions.  (Note: I'm just pretending that amazon.com would
support this kind of thing.)  Similarly, another package,
B_Tree_Library_Package, might allow the program to use a file as a
library, where all the entries in the library are organized as a
B-tree, and so on.

So given that Scan_By_Title, More_Books, and Next_Book would have to
be coded specially for each implementation of Library_Package, I don't
see how setting up a generic iterator the way you've done it solves
the problem.

Also, when you say that the body of Iterator calls abstract primitives
on Libraries, I don't see how those primitives would be written.  It
seems like the abstract primitives I'd have to define in Libraries
would be, essentially, the same Scan_By_Title, More_Books, Next_Book,
etc. routines I already tried to define, and those would have the same
problem with OUT parameters that I already complained about.  So it
seems like all you've done is pushed the problem one level back.  I'll
grant that you could implement these primitives (perhaps in the
private part of Libraries) using a "dirty" method that uses accesses
to the abstract types as OUT parameters, and provide Iterator as a
"clean" interface to the outside world to avoid the dirty stuff.  Is
this what you were getting at with your response?  (Personally,
though, I don't see much benefit from adding this extra layer.)

>  > What is the cleanest way to accomplish what I'm trying to do?  This
>  > looks like such a typical use of polymorphism that I'd be surprised if
>  > there were no intuitive way to do it.
>
>    Now to write your Scan_by_Title, lets make it a child of Libraries:
>
>   generic
>     type Some_Library is new Library with private;
>   procedure Libraries.Scan_By_Title (Lib : in Some_Library;
>                                  Reg_Expression : in String;
>                                  Scan           : out Scan_Info;
>                                  RE_Error       : out Boolean);
>
>   procedure Libraries.Scan_By_Title (Lib : in Some_Library;
>                                  Reg_Expression : in String;
>                                  Scan           : out Scan_Info;
>                                  RE_Error       : out Boolean) is
>     ...
>     package Local_Iterator is new Libraries.Iterator(Some_Library, Lib);
>   begin
>     ...
>     while Local_Iterator.More loop
>       Process_Book(Local_Iterator.Next, {other parameters});
>     end loop;
>   end Libraries.Scan_By_Title;
>
>   I made this a child of Libraries for exposition purposes, you may
> want to have Scan_By_Title as an abstract primitive of the Library
> type and in effect, require that all non-abstract types derived from
> Library provide a body, or you could make it a non-abstract primitive.
> My leaning is to provide the Iterator as a child of Libraries, but to
> provide the search function as an abstract primitive.  In other words,
> some Library implementations may not have a concept of an ordering
> among books, but might still provide a search mechanism.

Unfortunately, you've reduced some flexibility by doing this, because
there's no guarantee that Process_Book (whatever it is---you haven't
defined it---is it a generic subprogram parameter?) is going to be
easy to write.  I often define iterators as four routines (initialize,
test-for-more, get-next, close) instead of, say, writing a generic
"do-this-for-all-the-books" routine (or a "do-for-all" routine that
takes an access-to-subprogram parameter), precisely to give myself
this flexibility.  For example, I might want to stop the iterator
after 10 books, because I only have room on the screen to display 10,
and I want to wait for the user to input a "go to next page" command
before bothering to retrieve the next 10.  This would be especially
necessary when FTP'ing from amazon.com; you don't want to have to
query for the entire set of matches until you know you'll need them.
Or, maybe I want to display three books on each line:

    begin
        Scan_By_Title (Lib, Title_Pattern, List_Scan, Error);
        if Error then ... end if;
        while More_Books (List_Scan) loop
            Next_Book (List_Scan, Book);
            Title1 := new String' (Title (Book));
            if not More_Books (List_Scan) then
                Title2 := null;
            else
                Next_Book (List_Scan, Book);
                Title2 := new String' (Title (Book));
            end if;
            if not More_Books (List_Scan) then
                Title3 := null;
            else
                Next_Book (List_Scan, Book);
                Title3 := new String' (Title (Book));
            end if;
            Text_IO.Put_Line (Make_One_Line_Out_Of_Three_Titles
                                 (Title1, Title2, Title3));
        end loop;
        Close_Scan (List_Scan);
    end List_Books_With_Matching_Titles;

My point here is that there may well be cases where a simple
"execute-the-same-code-for-every-item-iterated" approach isn't the
best, and I don't see a reason why I would want to be locked into that
approach, the way your Scan_By_Title routine seems to.  (The above
example would actually be pretty easy to fit into the Process_Book
approach, but that's not relevant.)

=============================

I'll look at my real-life code to see if it would benefit from coding
an iterator as you suggest.  For now, though, I've worked around my
problem by using access types for the OUT parameters.

                                -- Adam

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




             reply	other threads:[~1998-04-30  0:00 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-04-30  0:00 adam [this message]
1998-05-06  0:00 ` How to Use Abstract Data Types Robert I. Eachus
  -- strict thread matches above, loose matches on Subject: below --
1998-05-04  0:00 adam
1998-05-06  0:00 ` Robert I. Eachus
1998-04-22  0:00 adam
1998-04-23  0:00 ` Jacob Sparre Andersen
1998-04-30  0:00 ` Robert I. Eachus
     [not found]   ` <matthew_heaney-ya023680003004981709380001@news.ni.net>
1998-05-05  0:00     ` Stephen Leake
1998-05-05  0:00       ` Matthew Heaney
1998-05-06  0:00         ` Stephen Leake
replies disabled

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