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

Re: Procedures (interfaces)

This page is part of the web mail archives of SRFI 44 from before July 7th, 2015. The new archives for SRFI 44 contain all messages, not just those from before July 7th, 2015.

> 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.

>> 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.

>> 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

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

>> 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