On 2/24/07, Jonathan Lang wrote:
In effect, using * as an array of indices gives us the ordinals notation that has been requested on occasion: '*[0]' means 'first element', '*[1]' means 'second element', '*[-1]' means 'last element', '*[0..2]' means 'first three elements', and so on - and this works regardless of what the actual indices are.

Using * that way works, but it still is awkward, which makes me think there's something not quite dropping into place yet. We have the notion of "keyed" indexing via [] and "counting"/ordinal indexing via [*[]], which is rather a mouthful. So I end up back at one of Larry's older ideas, which basically is: [] for counting, {} for keys.

To put a slight twist on it: instead of adding {}-indexing to arrays, consider that what makes something an "array" is that it doesn't have keys -- it's a collection of things that you can count through, as opposed to a collection that you search through by meaningful keys/names/tags/references/etc. (E.g., consider positional vs. named params, and how they naturally map onto an array and a hash respectively.)

Now something that is countable doesn't have to have meaningful keys, but any keyed collection can be counted through; hence it makes sense to give hashes an array-like [] accessor for getting the first/last/nth item in the hash. In fact, this is basically what %h.values gives you -- turning the hash values into an array (well, a list). Saying %h[n] would amount to a direct way of saying @(%h.values)[n].

This becomes much more handy in P6, because hashes can be ordered. (Not that there's anything stopping you from counting through an unordered hash; %h[0] is always the first element of %h, you just might not know what that is, the same as with %h.values.) If Perl knows how to generate new keys on the fly (say, because your possible hash keys were declared as something inc-/dec-rementable), then you can even access elements off the ends of your hash (push/unshift).

What about shaped arrays? A "shape" means the indices *signify* something (if they didn't, you wouldn't care, you'd just start at 0!). So they really are *keys*, and thus should use a hash (which may not use any hash tables at all, but it's still an associative array because it associates meaningful keys with elements). I'm not put off by calling it a hash -- I trust P6 to recognise when I declare a "hash" that is restricted to consecutive int keys, is ordered, etc. and to optimise accordingly.

If there are no meaningful lookup keys, if all I can do to get through my list is count the items, then an array is called for, and it can work in the usual way: start at 0, end at -1. It is useful to be able to count past the ends of an array, and * can do this by going beyond the end: *+1, *+2, etc., or before the beginning: *-1, *-2, etc. (This neatly preserves the notion of * as "all the elements" -- *-1 is the position before everything, and *+1 is the position after everything else.)

Well, at least this keeps the easy stuff (counting) easy, and the barely-harder stuff (keying) possible. In fact, since hashes would always have both views available, nothing is lost; we get ordinals for hashes, shaped collections, and ones that you can pass to a sub without losing their shape, it solves the problem of distinguishing between ordinal vs. "funny" indices (and the related issues of wrap-around), you can count past the edges, and all while preserving familiar array behaviour (especially for P5 veterans), the meaning of * as "everything", and uncluttered syntax.


Reply via email to