Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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 indexes *only* in slices #3008

Closed
penguindark opened this issue Dec 7, 2019 · 14 comments
Closed

negative indexes *only* in slices #3008

penguindark opened this issue Dec 7, 2019 · 14 comments
Labels
Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one.

Comments

@penguindark
Copy link
Member

Motivations
Hi,
I'm new to v, thus have mercy with me :)
I've found with the help of @medvednikov ( thanks for your kindness ;) ) an old discussion about negative indexes in array and string.
I understand very well the position of not have them in the arrays and string and I support it!!
But...
I was wondering, if v now has the slices syntax, why don't add negative indexes only in slices?
What are in your opinions the downside and upsides of this possible feature?
In my opinion have negative indexes only for slices can bring an advantage on the correctness and security of the code, but may be I'm wrong.
I'd like have other opinions on this possible feature.
Its implementation is not complicated.
Thanks in advance to all the contributors to this discussion.

to add a sample, it can work like this:

Example of usage in strings

//
// test negative indexes in slices
//
s := '0123456789'

// normal behaviour
assert s[1..3] == '12'
assert s[..3] == '012'
assert s[8..] == '89'

// negative indexes behaviour
assert s[-2..] == '89'
assert s[..-8] == '01'
assert s[2..-2] == '234567'
assert s[-12..-16] == ''
assert s[-8..-2]== '234567'

// out of bound both indexes
assert s[12..14] == ''
assert s[-12..16] == '0123456789'
//
// test negative indexes in slices
//
a:=[0,1,2,3,4,5,6,7,8,9]

// normal behaviour
assert a[1..3].str() == '[1, 2]'
assert a[..3].str() == '[0, 1, 2]'
assert a[8..].str() == '[8, 9]'

// negative indexes behaviour
assert a[-2..].str() == '[8, 9]'
assert a[..-8].str() == '[0, 1]'
assert a[2..-2].str() == '[2, 3, 4, 5, 6, 7]'
assert a[-12..-16].str() == '[]'
assert a[-8..-2].str() == '[2, 3, 4, 5, 6, 7]'

// out of bound both indexes
assert a[12..14].str() == '[]'
assert a[-12..16].str() == a.str()
@penguindark penguindark added the Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one. label Dec 7, 2019
@penguindark
Copy link
Member Author

My opinion
The implementation is very simple. In my fork I testd yet it and work quite well with few line of code.
This feature is very useful in my experience.
For example, if you have a list of sentences and want filter all the list for only the last N chars.
It is not about reduce key strokes, but about to reduce possible bugs in code.
About negative indexes in arrays themselves, I'm against them, but in the slices they are very useful.

Here the code that explain the previous example:

No slice negative index

fn main() {
  sentences:=[
   'first sentence, number one',
   'second sentence, number two',
   'third sentence, number tre',
   '0123456789*123456789',
   '012345',
  ] 
  cut_len := 10
  mut res:=[]string
  for sent in sentences {
    if sent.len < cut_len {
      res<<sent
    }else{
      start_cut := sent.len-cut_len 
      res<<sent[start_cut..]
    }
  } 
  print(res)
}

Output

["number one", "number two", "number tre", "*123456789", "012345"]

Slice negative index

fn main() {
  sentences:=[
   'first sentence, number one',
   'second sentence, number two',
   'third sentence, number tre',
   '0123456789*123456789',
   '012345',
  ] 
  cut_len := 10
  mut res:=[]string
  for sent in sentences {   
      res<<sent[-cut_len ..]
  } 
  print(res)
}

Output

["number one", "number two", "number tre", "*123456789", "012345"]

@joe-conigliaro
Copy link
Member

joe-conigliaro commented Dec 10, 2019

This feature was discussed and left out on purpose for simpliciy and one way

@penguindark
Copy link
Member Author

Hi Joe, thanks for your explanation :) I appreciate it.
I see your example:

assert s[-2..] == '89'
assert s[..-8] == '01'
assert s[2..-2] == '234567'
assert s[-12..-16] == ''
assert s[-8..-2]== '234567'
vs

assert s[s.len-2..] == '89'
assert s[..s.len-1] == '01'
assert s[2..s.len-1] == '234567'
assert s[s.len-12..s.len-16] == ''
assert s[s.len-8..s.len-2]== '234567'

but these cases does not manage the out of the bound cases, like:

index1:= ... some calculation not solvable at compile time ...
assert s[2..index1]=='234567890'

that bring at runtime error, and this can happen in runtime at each slice operation with variable indexes.

if you want a safe code you must write for each slice something like this:

index1 := x
index2 := y
if index1>0 and index1<s.len and index2>index1 and index2 <s.len {
  res := s[index1..index2]
}else{
 ...manually manage negative indexes...
}

or something similar.
IMHO check the bounds and the use of negatives indexes only in slices in the compiler bring more safety in the code,
otherwise each single slice with a variable need a check around it.
Just my two cents :)

P.S. about simplicity, I've already test and implementated in my fork negative indexes only in slices for array and strings with very few lines of code.

@joe-conigliaro
Copy link
Member

joe-conigliaro commented Dec 11, 2019

Hi @penguindark, Thanks for your response :)

Im not exactly sure what you mean about the out of bounds stuff?
Im not sure how the other syntax makes it any different. Would you expect the out of bounds examples you showed to panic or return an empty array? if it panics as v currently does with out of bounds array you would need to add your own checks anyway

IMO you should always check indexes are valid before trying to access them, unless you know fot a fact they are

@penguindark
Copy link
Member Author

Hi @joe-conigliaro, thanks for the answer and I understand your position.

I'll try to explain better my proposal.
The slice is not a new feature, it exist from fortran66 and after few version, almost all the imperative languages implement it because it is very useful and reduce the code written and reduce the bug on array access.
Just a biref story of slicing: Slicing wikipedia

I'm simple saying that v, it seems, is trying to implement slice in a "perl/python/D" fashion :)
And I'm proposing to use the full "implementation" adding the negative index and bound control only for the slice operation not for the array/string.

All the people that came from Python,Perl,R,D or other modern language tend to use slice very often and expect a familiar behavior for slices.

For example I came here from Deep Learning with C/C++/Python background, and having a slice where I must check the bound make me very uncomfortable, and not have negative indexes in the slicing operation not help me to choose v for DL projects.

All the people that works with big data and DL usually use python or similar language with numpy,tensorflow and so on, and they expect slicing work in the simplest way possible and negative indexes and bounds check (only on slices) are taken for granted.

Not having them, for example, will be a big problem, I was trying to use DL libraries from V, but it is not simple for each array operation I need to do minum two check, it is repetitive and prone to error.

When in v appeared the slicing I was very happy, but not having (full slice) forced me to write a lot of boilerplate check code, and I'm not sure that v will be the right choice to me to continue my DL project, simple this.
Just my two cent from my little experience as developer.

Whatever the decision on slicing will be, I'll remain a fan of v and I'm trying to contribute to the development with my little free time. :)

P.S.
Your question before I forgot ;)

Im not exactly sure what you mean about the out of bounds stuff?

I mean that if we write as the following example (whit bounds "limitation") compile and run as written:

a:='0123456789'
assert a[..20]=='0123456789' // true
assert a[-20..]=='0123456789' // true
assert a[-4..20]=='6789' // true
assert a[20..]='' // true

D, for example, use a $ as array len, but I don't like it ;) :

auto c = a[$ - 4 .. $ - 2];

excuse the length of this post, I apologize for this and thanks for have read it.

@runeimp
Copy link

runeimp commented Dec 11, 2019

No negative indexes in Go slices is one of the few things I truly miss from Python and Ruby. With V having pulled several facilities from Python, such as in over := range in Go, I had expected it to utilize negative indexes in slices as well. V has done so well covering the few bases that Go doesn't cover in my opinion it is very surprising to hear that negative indexes in slices is quite surprising.

@penguindark
Copy link
Member Author

Just to give one more example of the possible advantage:

actual code:

fn main() {
  file_list:=[
   'f1.jpeg',
   'f2.jpg',
   'file_number_3.jpeg',
   'a.v',
   'text.txt',
  ] 
  
  for file_name in file_list {   
     if file_name.len > 4  && 
      	( file_name[file_name.len-4..].to_lower() in ['jpeg','.jpg'] )
      {
      	println("$file_name")
      }
  } 
}

with neg index and bound control will be:

fn main() {
  file_list:=[
   'f1.jpeg',
   'f2.jpg',
   'file_number_3.jpeg',
   'a.v',
   'text.txt',
  ] 
  
  for file_name in file_list {   
      if file_name[-4..].to_lower() in ['jpeg','.jpg'] {
          println("$file_name")
      }
  } 
}

@ylluminate
Copy link
Contributor

ylluminate commented Jan 22, 2020

My 2¢: negative indexing does feel more natural in various cases. I think it's worth doing this one since it's fairly simple and highly expressive. Hmmmm.

Maybe consider adding your additional proposal here from Discord @penguindark? That might be a nice middle ground for the (somewhat understandable) resistance.

@penguindark
Copy link
Member Author

@ylluminate yeah :)
The "new" proposal is to add # before the square bracket in the slice to use the slice with negative index and boud safe as described before.

a:='0123456789'
assert a#[..20]=='0123456789' // true
assert a#[-20..]=='0123456789' // true
assert a#[-4..20]=='6789' // true
assert a#[20..]='' // true

a[20..] ==> ERROR

where the # symbol can be the indicator for "safe but slower" operations 😉

@ntrel
Copy link
Contributor

ntrel commented Jul 20, 2020

In D you can use $ for the array length for indexing or slicing (syntax: I think it comes from using '$' as the end of a line in regular expressions):

a[$-1] // last element
a[1..$] // slice all but first element

I think something like that is much less bug-prone than supporting a negative index variable.

@penguindark
Copy link
Member Author

The meaning of negative indexs is not to have a "syntactic sugar" like the D $ operator instead using a.len ,
The meaning, IMHO is: "return me always a valid array and don't care about length calculation"
That this solution is more bug-prone is a thing we can discuss about :)
please have a further look at the example code here:

a:='0123456789'
assert a#[..20]=='0123456789' // true
assert a#[-20..]=='0123456789' // true
assert a#[-4..20]=='6789' // true
assert a#[20..]='' // true

@runeimp
Copy link

runeimp commented Jul 24, 2020

I don't think negative indexes should be doing any magic any more than positive indexes do.

a := '0123456789'
-if-
a[20] == '0' // error: index out of range
-then-
a[-20] == '0' // error: index out of range
-but if-
a[5] == '5' // true
-then-
a[-5] == '5' // true

In my mind the point of negative indexes is simply to grab a chunk from the back of a slice as easily as the front of the slice. Both should require safety checks. You can always convert a negative integer to a positive one, (intvalue * -1), and compare to a.len to see if it fits. No special hash syntax required. Otherwise don't allow ints, only uints.

@ntrel
Copy link
Contributor

ntrel commented Jul 24, 2020

Given that V uses a signed integer for indexes, allowing negative indexes is just too bug prone when the index is a variable. That variable might be accidentally negative, and only rarely so the problem is hard to discover during testing.

@penguindark
Copy link
Member Author

In this idea you can not use negative index in normal arrays.
You must use a prefix before the square bracket eg: a#[..-3] otherwise you will use the normal and quicker array that supports only positive indexes :)

@vlang vlang locked and limited conversation to collaborators Sep 22, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Feature/Enhancement Request This issue is made to request a feature or an enhancement to an existing one.
Projects
None yet
Development

No branches or pull requests

6 participants