Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Negative indices #827

Closed
satyr opened this issue Nov 3, 2010 · 15 comments
Closed

Negative indices #827

satyr opened this issue Nov 3, 2010 · 15 comments

Comments

@satyr
Copy link
Collaborator

satyr commented Nov 3, 2010

$ perl6 -e "say (1..9)[* - 1]"
9

Borrowing from Perl6, we could make it work by replacing the preceding * in the index operator with its operand's length. Something like:

$ coffee -bpe "array[* - 1]"
array[array.length - 1];

$ coffee -bpe "makeArray()[* - 1]"
var _ref;
(_ref = makeArray())[_ref.length - 1];
@DmitrySoshnikov
Copy link

The idea is good, though regarding ES nature has some subtle cases with inheritance -- since "-1" is just a simple string property. It was also discussed for the ECMAScript itself (http://bit.ly/bX8dzm, http://bit.ly/8ZRbCM).

Python uses just negative indices (without *):

[1, 2, 3][-1] # 3

[* - 1] would avoid possible ambiguities with inheritance (e.g. what if there is a property "-1" placed on Object.prototype -- should sub-objects inherit it or use own semantics).

Also, in case of JS/Coffee, this thing should be generic, i.e. for any object which has length and possibly numeric-indecies. And probably even not depending on length -- since for the same call-site with foo[-1] in one call foo may contain length and in another -- may not -- should it then inherit "-1" from Object.prototype or should negative indices always be handled in this foo.length - n semantics? For the CoffeeScript it easier to make a decision i favor of later, for the ECMAScript it's harder.

Dmitry.

@satyr
Copy link
Collaborator Author

satyr commented Nov 3, 2010

"-1" is just a simple string property

this thing should be generic, i.e. for any object which has length

Yup, that's the idea.
This would also allow generic push in the form: arrayLike[*] = item

@nathany
Copy link

nathany commented Nov 4, 2010

Lua has a length operator, whereby #foo is equivalent to foo.length. It can be used anywhere as a little shortcut.

D gives special treatment to $ inside [ ], so for a[1..$]. I don't know if it does/needs a[$ - 1] or a[$], but that's basically the same as your Perl example (except I think it's the index of the last element instead of the length).

$ makes more sense to me in that it's the Regex special character for end of string, but then I've never worked with Perl.

@satyr
Copy link
Collaborator Author

satyr commented Nov 4, 2010

satyr/coco@ade1153

@TrevorBurnham
Copy link
Collaborator

Excellent idea! Very common use case, and it's nice to see a precedent in another language. +1

@jashkenas
Copy link
Owner

This is nice for Coco -- but all the same arguments against it still apply from previous tickets. What we really want is true negative indices -- and that's something that has to be built in to the VM itself.

@TrevorBurnham
Copy link
Collaborator

What arguments from what previous tickets? I found two issues, from February and May, the latter being the more relevant (benlambert proposed at one point that @ be used in the way that satyr proposed that * be used). But discussion was far from exhaustive or focused.

You know that this will never be built into the VM itself, since arr[-1] = 'foo' will always assign the value 'foo' to the key '-1' on the arr object. The choice is to adopt this nice syntax from Perl, or to write the long and redundant-looking arr[arr.length - x], or to use a wrapper function. Given that arr[arr.length - x] is a common idiom, this is a real problem. A non-ambiguous solution from another language shouldn't just be ignored.

The only cost is that people seeing * used this way for the first time will ask, "What's that doohickey?" But this is a question that can be answered immediately by looking at the JavaScript output. And from that point forward, that person will be able to write more succinct, readable code. Aren't those exactly the criteria for a good feature in CoffeeScript?

@jashkenas
Copy link
Owner

Those previous tickets cover alternative syntaxes for negative indexing pretty thoroughly. That's about as exhaustive and focused as these conversations ever get -- and there was also healthy discussion on IRC.

But what the hell, let's reopen it. My main beef with * is that it's cryptic and meaningless ... my main beef with $ is that it's a valid variable already (and probably a defined one at that).

That said, my favorite of all the options proposed so far is benlambert's directed indexing operation, which means "count from the back of the array, (zero-based)":

array[< 0] is array[array.length - 1]

@satyr
Copy link
Collaborator Author

satyr commented Nov 5, 2010

* or not, the Perl6 approach (length substitution) is much more flexible than Perl5's (just negative index).

a[*] = b      # append
a[*>>1]       # mid-item
a[*-1>>1<<1]  # the last item with even index

@DmitrySoshnikov
Copy link

@jashkenas

My main beef with * is that it's cryptic and meaningless ...

Meaningless -- yes, possibly. Although, what satyr showed regarding Perl 6, i.e.

a[*>>1] # mid-item
a[*-1>>1<<1] # the last item with even index

is more than cryptic (though, maybe just for me, since I do not know Perl) and I don't think that such a complicated constructs are the Coffee's way. But, single [< n] may seems good, though also cryptic. The same as [* - n] then.

my main beef with $ is that it's a valid variable already (and probably a defined one at that

Yep. However, if to treat this symbol specially inside the [ ], than it may be acceptable also (the same as $ is treated specially in regexp). But I agree that a normal identifier issue may take place.

P.S.: By the way, do you further plan to use JavaScript as an implementation level? I mean, not to implement it on some lower level language with VM, JIT, etc? If so, notice also, that ES6 (Harmony), as I mentioned above (http://bit.ly/8ZRbCM), possibly will have overloaded [[Get]] and [[Put]] semantics for array indicies with handling the negative ones. That means, that ECMAScript itself will have Python's semantics with just a foo[-1].

Currently, this semantics can be already implemented in JS 1.8.1 (FF4) with supporting Proxy objects, for example: http://bit.ly/dCnxNd http://bit.ly/9KBxzl.

So if now Coffee chooses some other syntax for that semantics, it further may simply switch to just foo[-n] scheme. The only thing is backward compats. for older code (i.e. if a user will differently treat a[-1] and a[* - 1]), but for Coffee it seems easier than for JS.

P.S.[2]:

Syntactically, both a[* - 1] and a [< 0] are good, though, a[<3] looks also a bit cryptic.

Dmitry.

@nathany
Copy link

nathany commented Nov 5, 2010

@satyr flexible yes, but it looks like a cat walked over your keyboard :-P

Until ES6, perhaps the simplest syntax is one Trevor suggested, just use a[-1..-1].

@satyr
Copy link
Collaborator Author

satyr commented Nov 5, 2010

a[-1..-1]

That's how we do it in JS when feeling DRY: a.slice(-1)[0]

@nathany
Copy link

nathany commented Nov 5, 2010

@satyr, simple enough.

If the plan (#830) is to remove instead of correct (#835) range slicing/splicing, then I'm not sure why we would add this feature.

@jashkenas
Copy link
Owner

Re-closing this ticket ... We really don't want to have special syntax inserted into indexing operations. obj[key] is going to preserve JS semantics, for better or for worse.

@joeytwiddle
Copy link

Just for fun:

Array::peek = -> @push @pop()   # It's a stack, we don't care about its length

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants