> Bradd W. Szonye wrote:
>> Set! compatible with R5RS - The original specification for *-set! was
>> incompatible with R5RS vector-set!, which modifies the vector in-place
>> and returns an unspecified value. I changed *-set! to match that, and I
>> changed *-set (the functional version) to return the modified
>> collection
>> and the previous value.
Taylor Campbell wrote:
> This doesn't work at all, because *-SET! is _linear_update_, not
> purely mutable. I am _adamantly_ against removing linear update
> procedures and replacing them with purely mutable ones ....
OK, that's fine, but then the *-set! procedure needs a different name:
1. In R5RS, *-set! is always purely mutable.
2. Vector-set! already exists with pure-mutable semantics.
>> *=? and <=?: I added question marks for consistency with functions
>> like string=?. I also removed the elt= argument and changed the
>> definition so that it was no longer necessary. I was reluctant to
>> make this change, but I felt that the original version just wasn't
>> viable.
> I don't like this at all. Removing ELT= is removing a _lot_ of
> functionality. The reason the ? wasn't there was _because_ the
> procedures were not comparable to STRING=?, CHAR=?, et cetera.
OK, I agree. How about this instead:
1. Rename the procedure to *-equal? so that you don't end up with both
string= (SRFI-44) and string=? (R5RS). In general, it's best to avoid
identifiers that differ by only one character.
2. For dictionaries, if elt=? is a procedure, use it to compare both
keys and values, and if elt=? is a pair of procedures, use (car
elt=?) to compare keys and (cdr elt=?) to compare values. For all
other collections, use elt=? or (cdr elt=?).
(Using the dictionary's key-equivalence function wouldn't work --
consider what happens when you try to compare two dictionaries with
different KEFs.)
By the way, one of the reasons I wanted to get rid of elt=? was to make
this more convenient for higher-order functions. If somebody can come up
with a reasonable default for elt=? and a syntax for omitting it, that'd
be great. If not, we can always use (lambda a (apply *-equal? eqv? a)).
> I don't understand the rationale for having ordering functions on
> _collections_. Can you explain this in more detail? (or in any
> detail at all: I can't find any)
Mainly because sets, sequences, and ordered collections all have
well-defined total ordering functions (proper subsets and
lexicographical comparisons). Also because it gives users a built-in
ordering function for creating ordered collections of collections (which
is fairly common in some programming styles).
>> procedure: % {key<? | key=?} {val<? | val=?} value ... => %
>> defined for: all collections
> The ordering functions here sort of make sense, I guess...but I don't
> like how they work with these two constructors. It's ambiguous what
> argument corresponds to what; for example, is (list = 1 2 3) a list
> with = for a comparison function and (1 2 3) for contents, or a list
> (#{Procedure} 1 2 3)? (where '#{Procedure}' indicates an abstract
> procedure value)
Sorry, I didn't explain the syntax. The {opt} indicates an optional part
of the interface, not an optional argument. For example:
procedure: bag val=? value ... => bag
procedure: dictionary key=? val=? pair ... => dictionary
procedure: ordered-dict key<? val=? pair ... => ordered-dict
The arguments aren't optional, but not all constructors use them.
>> procedure: *-copy * => *
>> defined for: all collections
>>
>> Returns a new collection of the same type as the argument with the same
>> contents but distinct storage. The copy is shallow; that is, it copies
>> values in a way that preserves object identity.
> I should like a *-COPY for sequences that also accepts optional start
> and end arguments.
Yes, that would be good. If provided, I'd prefer "start to end-1"
semantics, like C arrays and C++ iterators.
>> TYPE PREDICATES
>>
>> procedure: collection? value => value
>> procedure: %? value => value
>> defined for: all collections
>>
>> Returns a true value iff the provided value supports the interface for
>> the type (%) named in the predicate.
> Why %? and not *??
I didn't think it made a difference in this case. It's defined for all
collections, and it has "isa" semantics. If you think * makes more
sense, feel free to change it.
>> CARDINALITY OPERATORS
>>
>> procedure: *-empty? collection => boolean
> Don't forget the 'defined for: all collections' bit.
Oops, thanks.
>> procedure: *-size collection => exact integer
>> defined for: all collections
>>
>> If the collection has a concept of size, this function returns the
>> number of values or mappings in the collection. If it does not, #f must
>> be returned. If possible, this should return the number of times
>> collection-fold will call the fold-function before halting.
> Have we decided on the issues of infiniteness and circularity yet?
I think so, but I'm not certain. We did decide that *-size was a
sufficient replacement for finite-collection?. I think we also agree
that *-size should return the number of enumerations and that a circular
data structure is not necessarily infinite, but I'm not sure.
>> procedure: *-equivalence-function * => procedure
>> defined for: bags, sets, and dictionaries
>>
>> procedure: *-ordering-function * => procedure
>> defined for: ordered bags, sets, and dictionaries
> Where's the rationale for defining these _and_ [the keyed versions] on
> dictionaries?
To support the "simple" version of *=. Therefore, you can drop
dictionaries from the list if you stick with the original *= interface.
However, it just occurred to me that there's some ambiguity for
ordered-collection -- dictionaries are usually in key order, but other
collections are in value order. I think we actually need to separate
them into "ordered-collection" and "key-ordered-collection." Otherwise,
you'll have problems with polymorphism.
>> procedure: *-update * position f [thunk] => *
>> procedure: *-update! * position f [thunk] => unspecified
>> defined for: sequences and ordered collections
>>
>> procedure: *-update-key * key f [thunk] => *
>> procedure: *-update-key! * key f [thunk] => unspecified
>> defined for: sequences and ordered collections
> I don't understand why you're differentiating between these two, and
> why they aren't defined for dictionaries.
First, there's a typo: *-update is for sequences, *-update-key is for
dictionaries.
The reason for having two different functions is because key lookup is
*not* the same thing as indexing. I gave them similar names because
they're similar, but I didn't merge them because they're orthogonal.
Specifically, consider a dictionary that is also a sequence -- hash
tables, binary search vectors, and alists are good examples. By having
two functions, we can look up values by index or by key.
>> procedure: *-add-key[!] * new-key mapped-value [thunk] => *
>> defined for: dictionaries
>>
>> Returns a collection of the same type with the new key mapped to the
>> given value.
> Why have *-ADD with key/value pairs _and_ this?
Syntactic sugar, mostly. Also for consistency with the other -key
functions.
>> procedure: *-remove-every[!] * value => * list
>> procedure: *-remove-each-from[!] * source-collection [thunk] => % list
>> procedure: *-remove-every-from[!] * source-collection [thunk] => % list
>> procedure: *-remove-key[!] * key [thunk] => * pair
> Why differentiate between removing and removing keys?
Because they're two different operations: *-remove takes a value out of
a bag, and *-remove-key takes a key out of a dictionary. If you have a
dictionary that is also a bag, those need to be two different methods.
In other words, this is the same issue as update/update-key above.
>> procedure: *-remove-every-key[!] * key [thunk] => * list
>> procedure: *-remove-each-key-from[!] * source [thunk] => * list
>> procedure: *-remove-every-key-from[!] * source [thunk] => * list
> Er, why all of the name changes?
Hm, thought I already explained this. I couldn't get my head around
"all" and "any" -- kept thinking that "all" was the one that got rid of
all the copies. I dunno whether "every" and "each" is better, but I
*strongly* dislike any & all. I chose "each" to mimic "for-each."
>> procedure: *-insert-right * new-value [thunk] => *
>> defined for: reversible sequences
>>
>> procedure: *-delete-right * [thunk] => *
>> defined for: reversible sequences and reversible ordered collections
> Don't you mean 'reversible _flexible_ sequences?'
The original version allowed insert-right (but not insert-left) even for
non-flexible sequences. And the definition of non-flexible (limited)
sequences does permit addition at the end for some sequences.
My assumption here would be that fixed-size sequences (and others which
do not support end-extension) would call thunk.
--
Bradd W. Szonye
http://www.szonye.com/bradd