Operations on Collections
Cant:
ERROR [coll: Collection];
Raised when a collection is asked to perform an operation it can't (not because it's inappropriate, that raises Error; Cant signals that a sensible operation just isn't implemented).
QualityOf:
PROC [coll: Collection, op:
ATOM, args: ArgList ←
NIL]
RETURNS [ImplQuality];
Use this to investigate what operations a collection supports, and how well it does so. The quality depends on the collection, the operation, and certain arguments. Those arguments are the Direction, the bkwds: BOOL, or the remove: BOOL, if any. They are indicated to QualityOf as a LIST OF REF ANY, where each REF ANY is an ATOM whose name is the name of the enumerated value (e.g., $leftToRight, $FALSE). The op is the name of a procedure in this interface (other than QualityOf and derivatives) that calls class procedures.
ArgList: TYPE ~ LIST OF REF ANY;
ImplQuality:
TYPE ~ {cant, poorDefault, goodDefault, primitive};
A primitive is directly implemented by the class. A goodDefault is no worse than a small constant factor from a primitive. A poorDefault is worse than a goodDefault, typically by a factor of Size[coll]. `cant' means Cant will be raised (except for the cases of SpaceOf and OrderingOf, wherein a nil value will be returned).
GetBool:
PROC [args: ArgList, i:
NAT, default:
BOOL ←
FALSE]
RETURNS [
BOOL];
args.first ~ i=1
GetDir: PROC [args: ArgList, i: NAT, default: Direction ← leftToRight] RETURNS [Direction];
FromBool: PROC [BOOL] RETURNS [ATOM];
FromDir: PROC [Direction] RETURNS [ATOM];
Can:
PROC [coll: Collection, op:
ATOM, args: ArgList ←
NIL]
RETURNS [
BOOL]
~ INLINE {RETURN [QualityOf[coll, op, args]#cant]};
HasMember:
PROC [coll: Collection, elt: Value]
RETURNS [
BOOL]
~ INLINE {RETURN [coll.class.HasMember[coll, elt]]};
OrderStyle:
TYPE ~ {none, client, value};
Every collection is either ordered or unordered. An ordered collection considers its elements to be arranged in some order; an unordered collection does not. An ordered collection is either client-ordered or value-ordered. In a value-ordered collection, the order is determined solely by an Ordering among the elements. In a client-ordered collection, the order is determined by information (other than the elements) the client passes when creating or changing the collection.
OrderStyleOf:
PROC [coll: Collection]
RETURNS [OrderStyle]
~ INLINE {RETURN [coll.class.orderStyle]};
Ordering: TYPE ~ RECORD [Compare: CompareProc, data: REF ANY];
unordered: Ordering ~ [
NIL,
NIL];
An ordering is a closure that reveals the ordering relation between two values (in the same Space). unordered is an Ordering that isn't really an ordering.
ReverseOrdering:
PROC [o: Ordering]
RETURNS [ro: Ordering];
If o says a before b, ro says b before a.
ComposeOrderings:
PROC [mso, lso: Ordering]
RETURNS [Ordering];
If mso (most significant ordering) doesn't say equal, that's it; otherwise consult lso.
OrderingList: TYPE ~ LIST OF Ordering;
LexOrdering:
PROC [prefix, repeat: OrderingList]
RETURNS [Ordering];
Returns an ordering on LOVs. The first elements are compared with prefix.first; if not equal, that's it. Otherwise the second elements are compared with prefix.rest.first; if not equal, that's it. Otherwise continue, until either prefix or one of the LOVs is exhausted. If one of the LOVs is exhausted, return: equal if the other is, less or greater if the other isn't (dependening on whether the empty one was the first or second argument). If prefix is exhausted before both lists, pick up with repeat. If repeat is exhausted before both lists, use it over again.
SpaceOrdering: PROC [space: Space] RETURNS [Ordering];
OrderingOf:
PROC [coll: Collection]
RETURNS [Ordering]
Only meaningful for value-ordered collections.
~ INLINE {RETURN coll.class.OrderingOf[coll]};
Enumerate:
PROC [coll: Collection,
Consumer:
PROC [Value], bkwd:
BOOL ←
FALSE]
Passing bkwd~TRUE means to enumerate the elements in the opposite order from what would happen if bkwd~FALSE this call; some collections don't implement this, and raise Cant.
It's valid to operate on the collection from inside a Consumer, but there are no guarantees about whether elements added or deleted inside a Consumer are seen in that enumeration.
~ INLINE {coll.class.Enumerate[coll, Consumer, bkwd]};
PreserveValue:
PROC [coll: Collection, val: Value]
RETURNS [preserved: Value]
Note that the Consumer passed to Enumerate gets a REF ANY to indicate the value being enumerated. Now, since a REF ANY is, in general, a reference to a variable, that variable could vary; and that might mean a change of the value represented by that REF ANY. Now, for this enumeration mechanism to make any sense at all, that can't happen for the duration of any call on Consumer; but what about after Consumer returns? Another way to ask this is, is it valid for the Consumer to store the REF ANY somewhere for use after it returns? Let us say that it is not, in general. However, for the Consumer that simply must save a value for later use, we provide the PreserveValue procedure, which produces a REF ANY that will never change which value it represents. Of course, for many Collections this will not be an issue, and PreserveValue will return the same REF ANY it is given.
~ INLINE {RETURN [IF coll.class.PreserveValue=NIL THEN val ELSE coll.class.PreserveValue[coll, val]]};
Tester: TYPE ~ PROC [val: Value] RETURNS [pass: BOOL ← FALSE];
Scan:
PROC [coll: Collection,
Test: Tester, bkwd:
BOOL ←
FALSE]
RETURNS [MaybeValue]
A stoppable enumeration.
~ INLINE {RETURN coll.class.Scan[coll, Test, bkwd]};
ParallelTester: TYPE ~ PROC [a, b: MaybeValue] RETURNS [pass: BOOL ← FALSE];
ParallelScan:
PROC [a, b: Collection,
Test: ParallelTester, bkwd:
BOOL ←
FALSE]
RETURNS [ParallelFind];
The elements are produced in the order they would be produced by calls on Scan for the individual collections. The tester takes MaybeValues instead of Values because one collection may run out before the other.
ParallelFind: TYPE ~ RECORD [found: BOOL, a, b: MaybeValue];
TheElt:
PROC [coll: Collection]
RETURNS [Value]
Returns the element, if there is exactly one; Error["%g not a singleton", LIST[coll]] otherwise.
~ INLINE {RETURN coll.class.TheElt[coll]};
notASingleton: READONLY ROPE;
First:
PROC [coll: Collection]
RETURNS [MaybeValue]
Returns the first Value that Enumerate would enumerate, or noMaybe if coll is empty.
~ INLINE {RETURN coll.class.Extremum[coll, FALSE, FALSE]};
Last:
PROC [coll: Collection]
RETURNS [MaybeValue]
~ INLINE {RETURN coll.class.Extremum[coll, TRUE, FALSE]};
Pop:
PROC [coll: Collection, bkwd:
BOOL ←
FALSE]
RETURNS [MaybeValue]
Returns and removes the first or last Value that Enumerate would enumerate, or noMaybe if coll is empty.
~ INLINE {RETURN coll.class.Extremum[coll, bkwd, TRUE]};
Extremum:
PROC [coll: Collection, bkwd, remove:
BOOL]
RETURNS [MaybeValue]
~ INLINE {RETURN coll.class.Extremum[coll, bkwd, remove]};
Next:
PROC [coll: Collection, elt: Value]
RETURNS [MaybeValue]
Returns first the Value, not equal to elt, that would follow some instance of elt in enumeration, or noMaybe if all instances of elt are at the end. Ordered collections can do this even if elt is not in them; an unordered collection will raise Error["%g not in %g", LIST[elt, coll]].
~ INLINE {RETURN [coll.class.Get3[coll, elt].next]};
Prev:
PROC [coll: Collection, elt: Value]
RETURNS [MaybeValue]
~ INLINE {RETURN [coll.class.Get3[coll, elt].prev]};
Get3:
PROC [coll: Collection, elt: Value]
RETURNS [prev, same, next: MaybeValue]
equivalent to [coll.Prev[elt], IF coll.HasMember[elt] THEN [TRUE, elt] ELSE noMaybe, coll.Next[elt]].
~ INLINE {RETURN coll.class.Get3[coll, elt]};
Size:
PROC [coll: Collection, limit:
LNAT ←
LNAT.
LAST]
RETURNS [
LNAT]
Size = number of elements, or limit <= Size <= number of elements.
~ INLINE {RETURN [coll.class.Size[coll, limit]]};
Empty:
PROC [coll: Collection]
RETURNS [
BOOL]
~ INLINE {RETURN [coll.class.Size[coll, 1]=0]};
Mutability:
TYPE ~ {constant, readonly, variable};
A variable collection represents a collection variable: a variable that ranges over collections (but with fixed space, duplicity, and ordering). A readonly collection is a reference to a collection variable that does not give write permission to the holder (but other references to that variable might have it). A constant collection does not represent a variable; it represents a collection value.
ChangeableMutability: TYPE ~ Mutability[readonly..variable];
UnwriteableMutability: TYPE ~ Mutability[constant..readonly];
MutabilityOf:
PROC [coll: Collection]
RETURNS [Mutability]
~ INLINE {RETURN [coll.class.mutability]};
Copy:
PROC [coll: Collection]
RETURNS [VarColl]
Returns a new collection variable, initialized to the current contents of the argument.
~ INLINE {RETURN [coll.class.Copy[coll]]};
Insulate:
PROC [coll: Collection]
RETURNS [UWColl]
When applied to a variable collection, returns a readonly reference to that variable; otherwise returns the given collection.
~ INLINE {RETURN [coll.class.Insulate[coll]]};
ValueOf:
PROC [coll: Collection]
RETURNS [ConstColl]
When applied to a variable or readonly collection, returns a constant collection that is the current value of the variable; otherwise returns the given (constant) collection.
~ INLINE {RETURN [coll.class.ValueOf[coll]]};
Freeze:
PROC [coll: Collection]
RETURNS [const: ConstColl]
So what do you do when you've got a variable collection you want to pass to a procecure that wants a constant one? What if you also know that the variable is not going to vary for the duration of the call? It would be a pain to make a copy just to satisfy the mutability checking. So here's an alternative. Freeze[a variable collection] prohibits changes, and returns a collection that is allegedly constant. Attempts to update a frozen variable raise Error[frozen, LIST[coll]]. When, if ever, the variable is thawed, attempts to use the constant collection raise Error[unfrozen, LIST[const]]. Pairs of Freeze and Thaw nest like parentheses; a variable can only be changed if it is outside the scope of any Freeze...Thaw.
~ INLINE {RETURN coll.class.Freeze[coll]};
frozen, unfrozen: READONLY ROPE;
Thaw:
PROC [coll: Collection]
~ INLINE {coll.class.Thaw[coll]};
WhereKind: TYPE ~ {any, end, rel};
End: TYPE ~ {front, back};
WhereReln: TYPE ~ {before, after};
Where:
TYPE ~
RECORD [
SELECT kind: WhereKind
FROM
any => [],
end => [end: End],
rel => [elt: Value, reln: WhereReln],
ENDCASE ← any[]];
AddElt:
PROC [coll: Collection, elt: Value, where: Where ← []]
RETURNS [new:
BOOL]
For non-client-ordered collections, where must be [any[]], which means the client is not restricting where the element goes in the collection (if that even makes sense).
AddElt does not change whether a collection MayDuplicate. For non-duplicating collections, it is union with the singleton set {elt}. For duplicating ones, it adds a new occurrence of elt.
For non-duplicating collections, new tells whether there was already an equivalent element in the collection; for duplicating ones, new can be anything.
~ INLINE {[new, new] ← coll.class.AddColl[coll, CreateSingleton[elt, coll.SpaceOf], where]};
NAddElt:
PROC [coll: Collection, elt: Value, where: Where ← []]
RETURNS [new:
BOOL];
A non-INLINE version, for the benefit of interpreter calling.
AddColl:
PROC [coll, other: Collection, where: Where ← []]
RETURNS [someNew, allNew:
BOOL]
Like a series of AddElts. If coll is client-ordered, the elements of other appear together in the order they would come from Enumerate.
~ INLINE {RETURN coll.class.AddColl[coll, other, where]};
RemoveStyle:
TYPE ~ {any
--don't care--,
one,
first,
last, all};
When removing an element from a duplicating collection, the question arises: do you mean to remove every occurence, or just one? Which one?
RemoveElt:
PROC [coll: Collection, elt: Value, style: RemoveStyle ← any]
RETURNS [had:
BOOL]
~ INLINE {[had, had] ← coll.class.RemoveColl[coll, CreateSingleton[elt, coll.SpaceOf], style]};
RemoveColl:
PROC [coll, other: Collection, style: RemoveStyle ← any]
RETURNS [hadSome, hadAll:
BOOL]
RemoveColl is equivalent to a series of RemoveElts. Thus, when style is one, first, or last, and coll is duplicating, other must be able to enumerate (so we know how many instances of a member of other to delete); otherwise either being able to enumerate or test membership suffices.
~ INLINE {RETURN coll.class.RemoveColl[coll, other, style]};
Erase:
PROC [coll: Collection]
~ INLINE {[] ← coll.RemoveColl[coll]};
SpaceOf:
PROC [coll: Collection]
RETURNS [Space]
A collection is allowed to not know its space (in which case this proc returns NIL) if it doesn't need to know in order to implement the operations it implements.
~ INLINE {RETURN coll.class.SpaceOf[coll]};
MayDuplicate:
PROC [coll: Collection]
RETURNS [
BOOL]
Might this collection enumerate equal elements twice? Uninteresting, and unspecified, for collections that don't enumerate.
~ INLINE {RETURN [coll.class.mayDuplicate]};
Equal:
PROC [a, b: Collection]
RETURNS [
BOOL];
If a and b may not duplicate,