I need the opposite of `..<index`, why is there no `index>..`?

I have someIndex position right before the suffix I'm look for. So I need to get suffix like this:

collection[someIndex>..]

but there is no such postfix operator. So I have to calculate the after:

collection[collection.index(after: someIndex)...]

Why no index>..? Is there anyway to write this postfix operator?

From the existing one:

prefix static func ..< (maximum: Self) -> PartialRangeUpTo

I don't see how to create >.. without access to the collection to calculate index position?

Can you use a one-sided range?

collection[index...].dropFirst()

If this works, you would just need to make sure you have an explicit end condition to get you out of the loop since this kind of range would go in infinitely.

Rather than deleting the comment, this was corrected per @Lantua's comment below.

"Filling the gap" for range expression was pitched a few times before (you can search for Range Expression in #evolution), though I don't think much has come out of it.

FWIW, <.. isn't a valid operator right now as dot-operator must begin with dot.

PS

You can also do

collection[index...].dropFirst()
2 Likes

Please don't do this. That index + 1 is a valid and in-bound index is a very strong and fragile guarantee that works only on Array and ArraySlice. If you so much as to do .lazy.filter, you'd already have a problem.

1 Like

So you have to use the .dropFirst() method or set the index with an explicit assignment before using it in the range operation?

Given that the OP doesn't tell us what kind of collections we're concerning, it's probably either that, or manipulating the index directly using collection.index(after:), which is already mentioned in the question.

1 Like

With all that said, you can implement RangeExpression and add operator similar to ..< yourself if you're not let down by the fact that it won't be <.. or >..

It’s a BidirectionalCollection

As @Lantua explained up-thread

And expanding on it a bit, this is because of the limit of character combinations allowed in an operator for it to be parsed unambiguously.

If you need left-open right-closed intervals, I have an incomplete library Interval on GitHub. However, it has some significant disadvantages compared to RangeExpression types.

Not sure what RangeExpression mean here for defining an custom postfix operator to get partial range I want.

Anyway, I don't care what the operator is (a func is okay), only care for getting my suffix partial range without have access to the Collection. I give it a try:

postfix operator |||

extension Comparable {
    postfix static func ||| (minimum: Self) -> PartialRangeFrom<Self> where Self: Strideable, Self.Stride: SignedInteger {
        // only can do this
//        minimum...
        // how to advance minimum to exclude the first position?
        (minimum...).dropFirst()  // error: Referencing instance method 'dropFirst' on 'PartialRangeFrom' requires that 'Self' conform to 'Strideable'
        // ^^^ error: Cannot convert return expression of type 'DropFirstSequence<(PartialRangeFrom<Self>)>' to return type 'PartialRangeFrom<Self>'
    }
}

I failed :frowning:

Ok, yea, RangeExpression documentation is a little obscure. Though much of it is a convention and not strict API rules/guarantees.

In any case, if you'd like the postfix operator to feel like other range expressions, you're on the right track that comparable||| needs to return a new type that you implement. However, you also need to conform PartialRangeFrom to RangeExpression. That's where you'll get an opportunity to remove the first item, in RangeExpression.relative(to:).

postfix operator |||

struct PartialRangeFrom<Bound: Comparable>: RangeExpression {
  var bound: Bound
    
  func contains(_ element: Bound) -> Bool { bound < element }
  func relative<C>(to collection: C) -> Range<Bound> where C : Collection, Self.Bound == C.Index {
    (collection.index(after: bound)...).relative(to: collection)
  }
}
extension Comparable {
  postfix static func |||(bound: Self) -> PartialRangeFrom<Self> {
    .init(bound: bound)
  } 
}

let a = [0, 1, 2]
a[a.startIndex|||] // [1, 2]

In this case though, don't forget to document that bound cannot be endIndex, since we're not handling that right now (you can if you'd like to).

2 Likes

Thank you for showing how RangeExpression work.

So guessing at how things work, from something like this:

someCollection[someRangeExpression]

The compiler must be generating code that calls the someRangeExpression.relative(to:) passing in the Collection to get a Range. This answers my question of how an index can get a hold of a Collection to do index calculation.

that's very interesting that's calling .relative(to:) again...to convert a PartialRangeFrom to a Range...

Welp, that's what 1-minute coding does to your program :smirk:. Can prolly just do

collection.index(after: bound)..<collection.endIndex
1 Like

Also, yes.

1 Like

How come you can redefine PartialRangeFrom and not have conflict with the built-in one?

Things defined in the current module are prioritised over things defined in other modules. That way people can add things to libraries without worrying about breaking source stability too much. It's similar to how variables in inner scopes can shadow the ones in outer scopes. If you want to use the one from standard library, you can still write Swift.PartialRangeFrom

2 Likes