[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

ADT Lib (6)



IR> Writers have no other state than position.  After writing an
IR> object to a Bag, for instance, the only thing that's changed 
IR> state is the Bag.  An object without a state probably doesn't 
IR> need to exist.

I agree with you.  The term "Writer" has been used in a very vague
way
on this mailing list.  I tried to fill it with some meaning by
incorporating it into the rider, which is both a reader and a writer,
and using replacement sematics for writers.  We all know that Tim
does
not agree with this definition, but I haven't seen any other proposal
(disregarding Tim's hazy prose).  Until some other practicable
proposal is made, the current AcRider module stands as it is.

IR> The functionality for modifying containers would be done as
IR> methods of each container.  Add() for Set, Push() for Stack, 
IR> and AddToFront(), AddToEnd(), and others for List, etc.
IR> This approach also makes simple containers much easier to 
IR> use -- no intermediate writer object needed.

This is the reason why I removed `Add' and `Remove' from the
interface
of Container again.  There are simply too many variants, all with
wildly different semantics.

IR> Add/Remove methods for Container:
IR> ----------------------------------------------------
IR> I'm not sure why these can't be included.  Even set ADTs have Add
and
IR> Remove operations.  Are there ADT's that don't?
IR> 
IR> The semantics would be:
IR> 
IR> PROCEDURE (c: Container) Add* (obj: AcObject.Object);
IR> (* Ensures obj becomes a member of the container.
IR> The size of the container may or may not change.
IR> eg. for Sets this method will do nothing if obj is already in c.
IR> ADTs that allow multiple copies of same object in the container
IR> would always add obj to the container.
IR> *)
IR> 
IR> PROCEDURE (c: Container) Remove* (obj: AcObject.Object);
IR> (* Ensures that the membership count of obj in c is decremented
IR> by one.  If obj was in c once, then it is removed.  If obj was in
IR> c multiple times, then one copy of obj is removed (with no
guaruntee
IR> of which copy gets removed).  If obj is not in c then nothing is
done.
IR> *)

With such semantics, I can add these methods to container again.  The
question is: Is it worthwhile to have methods with such vague
semantics?  Does anyone second this?

Another question: With the above specification, is it guaranteed that
the statement sequence
  c.Add(foo); c. Remove(foo);
is a "no operation", i.e., will not change the state of `c' or the
system?

PF>    Okay, what about Hashtables, AVL-Trees or other data
structures that
PF> actually require the "elements" stored within to support a
certain set of
PF> operations like Hash(): INTEGER or LessThan (other: Ordered):
BOOLEAN? I
PF> have repeatedly stated my opinion that a generic Container-like
base class
PF> can only have "undisputable" methods like Empty(): BOOLEAN or
Elements():
PF> INTEGER. Nothing else.

Instead of "obj.Hash()" or "o1.LessThan(o2)" I prefer proper
functions, like "Hash(obj)" or "LessThan(o1, o2)".  The functions can
be implemented as a procedure variable, or as a type-bound procedure
of some base class.  See also Tim's posting about a "Compare" class
some time back: http://www.uni-kl.de/OOC/ooc-list/msg00528.html

The reason for this is simple: An object has no "natural" total
ordering.  If I have a "RECORD a,b: INTEGER END", then I can sort by
field `a' or `b' (or both), in ascending or descending order (but not
both ;-).  Therefore a single _method_ Object.Compare is not flexible
enough.  An external comparison _function_ gives this kind of
flexibility, though.

In other words: To insert an object in a hash table, one needs to
have
a hash function that can be applied to the object.  The object does
not need to have a property "hashable".

IR> 2. Regarding Mike's pointers to Booch components:
IR> 
IR> They use generics (ie. templates in C++) that Oberon-2 lacks.
IR> How to get around this?

Don't forget multi inheritance.

IR> 3. May I suggest that the res variable in rider be "sticky" --
that is,
IR>  once it sets to a non-done value, it holds that value and the 
IR>  rider refuses to do any more work until r.ClearError is called.
IR>  This greatly simplifies programming since res doesn't need to
IR>  be checked after every operation.

I will incorporate this change into AcRider.Mod.  This brings
container riders even closer to channel riders, which is a Good
Thing(tm) in my opinion.

TT> > This would be a severe case of bad design.  `Reset' is a rider
method,
TT> > not a method of the container.  Therefore it conflicts with
this rule:
TT> 
TT> No, reset is not a rider method because reset is not a *feature*
of the
TT> rider baseclass. If the rider base class (sequential iterator)
does not
TT> now about backstepping or random indexing it also does not know
about
TT> indexing with is similar to moving backwards several times or
random
TT> indexing with index 0. Only the container knows how to handle its
own
TT> iterators so it does belong to the container.

We will agree, that rider.Reset() is equivalent to r.SetPos(0)
(assuming an indexed rider for the moment).  And you will also agree
that `SetPos' is undisputable a rider operation.  It has to be a
rider
operation, because only riders have any sense of `position'; the
container does not now anything about positions.

We have two identic operations.  How can they differ in their
receiver
type?  You should rethink your argumentation, and maybe take a look
at
the "Common Mistakes" chapter of "OOC in O2".

Additionally, a function like c.Reset(r) is unsafe in the sense, that
`r' must have been created before by the very same container through
`c.NewRider'.  Therefore I am taking the "clean" way out and drop it
altogether.

-- mva