aarons@syma.sussex.ac.uk (Aaron Sloman) (12/16/90)
> > (Paul Johnson) writes: > >....If you have fully analysed your > >problem and know what routines are to be called where then you can > >code in Pascal or C just as easily. > rick@tetrauk.UUCP (Rick Jones) writes: > Organization: Tetra Ltd., Maidenhead, UK [...portion omitted...] > ....There are lots of > ways to produce a structured solution to a _specific_ problem; object > orientation is just one way to do it. But that doesn't make your system more > resilient to change, which is the real point. > > The most important point about reuse is the ability to reuse something in a way > that was not anticipated when it was written. Rigid hierarchies don't stand > much chance of letting you do that. [...portion omitted...] > > In a function oriented system, reuse is provided by function libraries. These > work quite well, but we all know that they are limited in scope because of > problems of encapsulation and extension. The acclaimed benefit of object > oriented methods is better reusability. [...portion omitted...] > > I feel this concern about MI is all to do with implementation problems of > languages which have been (short-sightedly) designed without due consideration > for the ultimately essential support of multiple inheritance. Many languages do have restrictions of this sort. However, some (though probably not all) of the facilitation of re-use that comes from OO Languages can also be achieved by languages that allow procedures to create functions and return them as results that can be used as input to other procedures that call them, e.g. by applying them to approriate data. This sort of facility is provided by Scheme, Common Lisp, Pop-11 (A Lisp-like language with a Pascal-like syntax that many find more readable), ML, and others. One requirement for this is either a polymorphic type mechanism (as in ML) or the ability to postpone type-checking till run time as in the other languages, which therefore provide greater flexibility. A toy example, to illustrate: suppose you want to have a re-usable function -create_pairs- that takes in two lists of objects (of any type), a function that creates two-element records, with one object from each list, and then makes and returns a sorted list of the two element records, where the required ordering of the sorted list depends on the types of objects in the list, etc. (How would you do this in an OO Language?). In Pop-11 you could define this as follows define create_pairs(list1, list2, makepair, testbefore) -> list; lvars item1, item2, list1, list2, procedure makepair, /* the pair creator */ procedure testbefore, /* the sort predicate */ list; /* make the unsorted list and assign it to list */ [% for item1, item2 in list1, list2 do makepair(item1, item2) endfor %] -> list; /* sort it, using the the library -syssort- and the supplied predicate */ syssort(list, testbefore) -> list enddefine; For example, given two lists of numbers, create a list of pairs of numbers, sorted by the sum of the pairs. vars numbers1 = [12 14 16 18], numbers2 = [28 25 22 19]; Assuming the pairs have been created using the system procedure -conspair- and can therefore be accessed using -front- and -back-, define the ordering predicate. define pairbefore(pair1, pair2) -> boolean; /* true iff sum of elements in pair <= those in second */ lvars pair1, pair2, boolean; front(pair1) + back(pair1) <= front(pair2) + back(pair2) -> boolean enddefine; /*Test create_pairs and print out the result */ create_pairs(numbers1, numbers2, conspair, pairbefore) => ** [[18|19] [16|22] [14|25] [12|28]] (This example does not use the most efficient coding). Note that this procedure can be applied to lists containing any type of data, provided that an appropriate pair creating and pair ordering procedure are available. I.e. The procedure -create_pairs- is re-usable for many different sorts of data-types, whose existence need not have been anticipated by the author. Re-use can be further simplified by using "properties" to associate with each type of entity, or each pair of types, the appropriate pair-creating and pair-comparing procedures. Then the create_pairs procedure would not need to be given the last two procedural arguments explicitly. It would use the contents of the first two arguments to get the appropriate procedures from the properties. A program (or development team) that dynamically created new types of data, and dynamically created appropriate functions for creating pairs and ordering them, and stored those functions in the appropriate properties, could use create_pairs on lists of objects of types that were not anticipated by the original programmer, in accordance with Rick's comment: > The most important point about reuse is the ability to reuse > something in a way that was not anticipated when it was written. Here, though, instead of information having to be associated with each type of object we allow extendable information to be associated with a particular generic procedure: via the two extendable properties. OO languages provide support for this through inheritance mechanisms. But there are other ways, as I hope my rather simple and artificial example illustrates. It should be clear how far more sophisticated examples could use the same technique, e.g. a re-usable network matching program for networks of various sorts, a re-usable database manager for storting data of different sorts, etc. This is common practice among Pop-11 programmers (as the -syssort- library program used in the example, illustrates, although the present version requires the user to specify the ordering predicate rather than associating one ordering predicate with each type of object, which would be rather restrictive). (However, because some users prefer to have an explicit OOP approach, including multiple inheritance, etc., Poplog Pop-11 now also includes an optional language extension to support it.) Aaron Sloman, School of Cognitive and Computing Sciences, Univ of Sussex, Brighton, BN1 9QH, England EMAIL aarons@cogs.sussex.ac.uk
rick@tetrauk.UUCP (Rick Jones) (12/20/90)
In article <4040@syma.sussex.ac.uk> aarons@syma.sussex.ac.uk (Aaron Sloman) writes: >[ ... ] Some >(though probably not all) of the facilitation of re-use that comes >from OO Languages can also be achieved by languages that allow >procedures to create functions and return them as results that can >be used as input to other procedures that call them [ ...] >A toy example, to illustrate: suppose you want to have a re-usable >function -create_pairs- that takes in two lists of objects (of any >type), a function that creates two-element records, with one object >from each list, and then makes and returns a sorted list of the two >element records, where the required ordering of the sorted list >depends on the types of objects in the list, etc. >(How would you do this in an OO Language?). OK, you issued the challenge :-) In Eiffel you would rely heavily on generic classes. First, a class to define a PAIR object (note - I've deliberately squashed normal Eiffel layout so as not to spread this article out too much): deferred class PAIR [T, U] export repeat COMPARABLE, item1, item2 inherit COMPARABLE redefine infix "<" feature item1: T; item2: U; infix "<" (other: like Current) is deferred end; end This defines the abstraction of a pair - i.e. it has two members, item1 & item2, whose types are generic, and is a COMPARABLE type, where the actual comparison operation is to be defined in a descendant class. (COMPARABLE is a library class which provides all the other comparison operators as derivatives of "<", so only that one has to receive a definition.) Now for the sorted list of pairs. Here we use an existing generic library class SORTED_LIST to create a deferred (abstract) class containing sorted pairs: deferred class SORTED_PAIRS [T, U] export repeat SORTED_LIST inherit SORTED_LIST [PAIR [T, U]] feature build (list1: LIST [T], list2: LIST [U]) is -- builds pairs into this list from two lists -- of relevant items do from list1.start; list2.start; until list1.off or list2.off loop -- this inherited routine puts the item at the -- correct place put (new_pair (list1.item, list2.item)); end; end ; -- deferred routine to create new pair objects new_pair (p1: T, p2: U): PAIR [T, U] is deferred end; end We then create non-deferred classes inheriting from the above to handle a specific type of pair. Some actual pair class would be like: class MY_PAIR export repeat PAIR inherit PAIR [INTEGER, STRING] define infix "<" feature Create (i: INTEGER, s: STRING) is do item1 := i; item2 := s; end; infix "<" (other: like Current) is do Result := item1 < other.item1; end; end This is an integer/string pair, where the comparison is done on the integer value. The sorted list of these pairs would be: class SORT_MY_PAIR is export repeat SORTED_PAIRS inherit SORTED_PAIRS [INTEGER, STRING] define new_pair feature Create (ints: LIST [INTEGER], strs: LIST [STRING]) is do build (ints, strs); end; new_pair (i: INTEGER, s: STRING): MY_PAIR is do Result.Create (i, s); end; end To create a sort_my_pair object, the call to create it requires the two lists of appropriate single values as arguments. Code fragment: ints: LIST [INTEGER]; strs: LIST [STRING]; mp: SORT_MY_PAIR; ... the first two lists get filled by some means ... mp.Create (ints, strs); This single call will create, populate, and order the list object "mp". With the proposed improvements to the methods of object creation planned for Eiffel, this solution could be even more elegant, in particular avoiding the need for a specific version of the SORTED_PAIRS class for each different PAIR class. What this does illustrate is the power of genericity, especially when combined with inheritance. I have found in practice that Eiffel's ability to handle truly generic classes is as important, if not more important, than inheritance. I don't think this example could be done anything like as easily in an OOPL with inheritance but not genericity. There are many roads to software reuse if you can navigate them properly! -- Rick Jones Tetra Ltd. Maidenhead, Was it something important? Maybe not Berks, UK What was it you wanted? Tell me again I forgot rick@tetrauk.uucp -- Bob Dylan