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

add support for unbounded array slicing/splicing #621

Closed
michaelficarra opened this issue Aug 16, 2010 · 17 comments
Closed

add support for unbounded array slicing/splicing #621

michaelficarra opened this issue Aug 16, 2010 · 17 comments

Comments

@michaelficarra
Copy link
Collaborator

Recall the array slicing operator: [x..y]. This operator extracts slices of arrays, and compiles like so:

a[b..c]
a.slice(b,c+1)

a[b...c]
a.slice(b,c)

I've collected some related examples that are not part of the language, but I believe should be, and what they should compile to.

a[b..]
a.slice(b)

a[b...]
a.slice(b,-1)

a[..b]
a.slice(0,b+1)

a[...b]
a.slice(0,b)

and if we want to get a little saucy:

a[..]
a

a[...]
a.slice(0,-1)

I'm not so sure about those last two yet, but they are ideas. Can I hear some thoughts on this? Also, remember that these slices can be assigned to, so this should be discussed as well.

@StanAngeloff
Copy link
Contributor

There are probably f-o-u-r closed tickets on this and at least three rejected patches. Try a search for 'Python' and 'slice'.

@michaelficarra
Copy link
Collaborator Author

Oh, I completely forgot to search for this before posting. Sorry.

@satyr
Copy link
Collaborator

satyr commented Aug 16, 2010

IMO, this sugar should be removed if it can't support this idea and/or negative indices.
It leads to awkward code like @properties[0...@properties.length - 1], which is better written as @properties.slice(0, -1).

@michaelficarra
Copy link
Collaborator Author

Yes, that is actually why I had the idea. Check this out:

# I want to slice(1) the returned array
@method(3,4)[1..]

# Instead I need to use temporary variables (in coffeescript! blasphemy!)
temp = @method(3,4)
temp = temp[1...temp.length]

and it still doesn't even compile to temp.slice(1)...

@TrevorBurnham
Copy link
Collaborator

Thanks for digging up those earlier tickets, StanAngeloff.

@satyr: I'm not sure what you're saying. @properties[0...-1] does compile to @properties.slice(0, -1).

@michaelficarra: As I recall, the main objection Jeremy had to making a[1..] compile to a.slice(1) is: What, then, is the behavior of a[1...]? I think the answer to that is a.slice(1, -1), but Jeremy's position is that this is less readable; and besides, Ruby—one of the biggest, most sugar-rich languages ever—doesn't allow such a syntax.

All of which we can debate endlessly, but let me just point out a couple of current inconsistencies that need to be resolved somehow:

First, a[0..], a[..0] and a[...0] are all syntax errors, but a[0...] compiles to a[0]. This is baffling.

Second, a lot of folks (myself included) have discovered this frustrating pattern:

['a', 'b', 'c'][0...-2] # ['a']
['a', 'b', 'c'][0..-2]  # ['a', 'b']
['a', 'b', 'c'][0...-1] # ['a', 'b']
['a', 'b', 'c'][0..-1]  # []

Sure, it makes perfect sense when you peek under the hood, but only then. Of course, there's no way to solve this while still piping ranges directly into slice, because you can't have a behavior that's different for [0..-1] and [0..x] when x is -1.

Suggestions?

@StanAngeloff
Copy link
Contributor

There was some talking about introducing a new syntax to deal with all the above limitations. The ... can be used to create splats, such as arguments.... When dealing with ranges, : can be used so things start to look a bit different. However this still leaves the case arguments[x] when x = -1 so back to square one.

It's just one of those impossible tickets... I for one have tried three different patches and none of them came close to solving all of the issues (though pretty close).

@weepy
Copy link

weepy commented Aug 16, 2010

I just implemented this in Kaffeine. I figured that A[x...] should just be the same as A[x..](that's what it looks like intuitively).

@TrevorBurnham
Copy link
Collaborator

Ah, I'm afraid this was one of Jeremy's stronger arguments, weepy: To me, it makes more sense for A[x...] to omit the last item, to be consistent with the behavior of ... vs. .. with an end index.

@weepy
Copy link

weepy commented Aug 16, 2010

yeh well u have to make a call either way. i think it's better to have the feature than worry about 'newbies' not understanding. After all it's only really JS wizards that will be using this stuff.

@jashkenas
Copy link
Owner

So, what's the proposal then? Would folks rather have slice literals removed from CoffeeScript, than have to specify the end of the slice? michaelficarra: what do you think after reading though those old tickets?

@satyr
Copy link
Collaborator

satyr commented Aug 17, 2010

TrevorBurnham: @satyr: I'm not sure what you're saying. @properties[0...-1] does compile to @properties.slice(0, -1).

Only accidentally so. It's a live code piece which I think is stemmed from the fact that a[0..-1] doesn't work.

Splicing is messy as well:

a = [1,2,3]
b = a[0...-1] = [4,5,6]
p a # [4,5,6,1,2,3] ([4,5,6,3] in Ruby)
p b # []            ([4,5,6]   in Ruby)

@TrevorBurnham
Copy link
Collaborator

Here's my proposal: Make the range/slice syntax compile to wrapper functions, when necessary, that give us all of the nice functionality we want, including:

  1. Making the slice [0..-1] return the whole array (as in Ruby)
  2. Making splice assignments work as in Ruby (see satyr's example above)

I still think the trailing syntax (being able to leave out 0 at the start of a range or -1 at the end) is nice, but that's a small sugar. The more important thing is to get rid of the "wtf?" issues described above.

Performance geeks can use slice directly; the rest of us shouldn't have to. Let's aim for consistency with Ruby.

Edit: This is pretty far afield from the original topic, so I'm going to make a separate issue for this proposal.

@michaelficarra
Copy link
Collaborator Author

@jashkenas: After sleeping on it and consulting a few programmer friends, I believe it is proper to allow unbounded slices/splices. This is mainly based on the use case I provided above.

  • I shouldn't have to use a temporary variable just to slice the first n elements of an array.
  • Allowing unbounded slicing will fit my use case into coffeescript's "everything is an expression" paradigm, and the compiled code is cleaner and nicer. It's a win for everyone.
  • Differences between .. and ... can be determined separately, but I wouldn't have a problem with any decision as long as it's clearly defined and consistent.
  • I don't believe anyone will confuse a slice that is unbounded to the right with the splat operator.
  • Allowing a[..] and a[...] is probably not a good idea.

@jashkenas
Copy link
Owner

Alrighty. I worked out how to get unbounded slices into the grammar ... by making splats only possible in the context of argument and parameter lists, where they belong. Here's the patch:

http://github.com/jashkenas/coffee-script/commit/4ddd65a4c4df5baa4911bf095246a7829e9acca6

Here's the test case from the patch:

# Slices and splices that omit their beginning or end.
array = [0..10]

ok array[7..].join(' ')  is '7 8 9 10'
ok array[-2..].join(' ') is '9 10'

ok array[...3].join(' ') is '0 1 2'
ok array[..-5].join(' ') is '0 1 2 3 4 5 6'

array[3..] = [9, 8, 7]

ok array.join(' ') is '0 1 2 9 8 7'

array[...3] = [7, 8, 9]

ok array.join(' ') is '7 8 9 9 8 7'

Take a look at this one if you have a minute, because I want to make sure that we have all of the off-by-one errors correct here.

@ghost
Copy link

ghost commented Aug 19, 2010

Glad to know this got in!

@StanAngeloff
Copy link
Contributor

THANKING YOU !

@michaelficarra
Copy link
Collaborator Author

Yes, I am very pleased with this patch as well. Thanks for adding it!

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