[Grace-core] Indexing

Michael Homer mwh at ecs.vuw.ac.nz
Wed Jul 2 03:34:11 PDT 2014


I mentioned this in passing in the other thread, but let's put it on a
stronger footing. (Summary at the end).

At the moment, indices are defined to be one-based. I think I might
have mentioned once or twice why I think that's a poor choice, but I'd
like to cover another issue. This is related to one of Kim's comments
in the other thread, about removing the currently-specified []
indexing:
> I approve!  It is more consistent OO to show a message send for these ops rather than having special syntax.
Let's go with that for a moment.

How OO is it to have a distinguished "number" entity that you must use
to access values (as opposed to doing actual arithmetic)? My answer is
"not very".

One part of that is simply that you should almost never be writing the
indices yourself: it's OO, that's what objects are for. You can write
(modulo chosen syntax):
    for (coll) doWithIndex { value, index -> ... }
which asks the object to run the block with each of the relevant
values. Here `index` is given to you. You can give it back to the
collection to get or replace that value, remove it, etc. It has
meaning in that context, but it shouldn't necessarily have any meaning
outside it: if you like, it's a capability token.

At the moment you can also write this:
    for (1..10) do { n -> print(coll.at(n)) }
but it isn't really OO. If "more OO" is better, you should only
interact with the collection through its methods, and its methods know
how it wants to identify its contents, so you don't have to.
Occasionally you might want `coll.at(1)` or `coll.at(coll.size)`, but
in both those cases it should really be the object figuring that out¹:
`.first`, `.last`, or something.

Sometimes the indices are meaningful. There are two main cases for
that: implementing collections with modular arithmetic, which I'll
forbear, and where they have some mapping to a real-world meaning. The
latter case is interesting here, because that mapping will only
occasionally be onto the natural numbers or a prefix of them.

To cover all of these cases, I'm suggesting that indices be specified
explicitly where they're to be used. Pascal (educational) and Ada
(non-broken) both do this. The language already has range objects,
which represent sequences of consecutive integers. Creating an array
might be something like:
    array.new(1 .. 10)
    array.new(0 .. 9)
    array.new(-180 .. 180)
A growable list could specify an unrestricted bound:
    list.new(0 .. Infinity)
Predefined sets of indices might be available. These cover all the
common use cases mentioned above: one-based indices for those that
want counting numbers, zero-based for modular arithmetic, and
arbitrary indices for the cases that map onto some real meaning. As
well as being more OO it's explicit about what's going on.

There could be some more esoteric sets of indices:
    array.new(1 .. 1000 .. 2) // Only odd numbers
    array.new(0 .. 100 .. 0.1) // Fractional indices?
    array.new("A" .. "Z") // Letters (supposing .. were defined)?
    array.new(myIndexRange) // Entirely user-defined sequence of index objects
Any set that is homeomorphically bijective with a prefix of ℕ is
suitable for use as an index set².

A collection that *didn't* have its index set specified would have an
opaque set by default: the indices you got back from it would have
meaning only to the collection they came from. This promotes an OO
structure, rather than procedural. They might be comparable to one
another by default, but they wouldn't be numbers; if you wanted them
to have meaning outside the object, you'd give them to the object when
you made it.

You may be able to ask a collection for the indices in use. With care,
it might even be part of the type.

Summary
=======
1) Specify indices of collections as ranges on creation, rather than
defaulting to 1 .. Infinity, because it's more OO and more explicit.
2) When indices are not specified, the collection creates opaque token
objects to represent them when required.
3) (Perhaps) permit user-defined sets of indices.
-Michael

¹ This has been Marco's point in the past, but I won't suggest going
so far as abolishing numbers.
² More specifically: there needs to be an order-preserving mapping
from the index set onto 0 .. n and back.



More information about the Grace-core mailing list