[Draft]: Introducing a striding(by:) method on 3.0 ranges

I can’t imagine any scenario in which getting the step's sign wrong wouldn’t just be a typo.

Well, it could be an error if you derive the stride by subtracting two numbers, and the second number is unexpectedly larger than the first. Of course, that makes it a *bug*, not a typo.

Why not just assign it the correct sign during the init function?
(0 ... 6).striding(by: 2) // [0, 2, 4, 6], end > start, so stride = by
(6 ... 0).striding(by: 2) // [6, 4, 2, 0], start > end, so stride = -by

One reason not to do it this way is that, if we extend `striding(by:)` to other collections, they will not be as easy to walk backwards through as this. You will have to do something like `collection.reversed().striding(by:)` which will be a hassle.

And the same may apply to Range, for that matter, because…

(OTOH, I don’t understand why 6…0 is currently a crash, rather than the, IMHO, obviously intended sequence of “6,5,4,3,2,1,0”, so maybe I’m not the best person to ask)

Well, suppose you say `6...0`. There are three ways that could be represented:

1. Range(start: 0, end: 6)
2. Range(start: 6, end: 0)
3. Range(start: 0, end: 6, reversed: true)

Option 1 throws away the fact that the range was ever reversed, so it doesn't actually help with this case.

Option 2 complicates testing. Where before you might have written this:

  let inBounds = start <= x < end

Now you must write:

  let inBounds: Bool
  if start < end {
    inBounds = start <= x && x < end
  }
  else {
    inBounds = start >= x && x > end
  }

Remember, conditional branches are relatively slow, and we want to avoid them where we can. If this is, for instance, the test of a loop, the extra branch is not a good thing.

Option 3 avoids this issue, but it requires more space. It also requires branches in other places which care about order.

And both 2 and 3 complicate slicing. If you say `array[6...0] = newValues`, the operation should probably be performed such that `array[6] == newValues[0]`. That seems difficult to implement—and all too easy to forget to handle. And I can imagine that it might prevent certain optimizations if the compiler couldn't prove they were forward ranges.

···

--
Brent Royal-Gordon
Architechies

I can’t imagine any scenario in which getting the step's sign wrong wouldn’t just be a typo.

Well, it could be an error if you derive the stride by subtracting two numbers, and the second number is unexpectedly larger than the first. Of course, that makes it a *bug*, not a typo.

Why not just assign it the correct sign during the init function?
(0 ... 6).striding(by: 2) // [0, 2, 4, 6], end > start, so stride = by
(6 ... 0).striding(by: 2) // [6, 4, 2, 0], start > end, so stride = -by

One reason not to do it this way is that, if we extend `striding(by:)` to other collections, they will not be as easy to walk backwards through as this. You will have to do something like `collection.reversed().striding(by:)` which will be a hassle.

And the same may apply to Range, for that matter, because…

(OTOH, I don’t understand why 6…0 is currently a crash, rather than the, IMHO, obviously intended sequence of “6,5,4,3,2,1,0”, so maybe I’m not the best person to ask)

Well, suppose you say `6...0`. There are three ways that could be represented:

1. Range(start: 0, end: 6)
2. Range(start: 6, end: 0)
3. Range(start: 0, end: 6, reversed: true)

Option 1 throws away the fact that the range was ever reversed, so it doesn't actually help with this case.

Option 2 complicates testing. Where before you might have written this:

  let inBounds = start <= x < end

Now you must write:

  let inBounds: Bool
  if start < end {
    inBounds = start <= x && x < end
  }
  else {
    inBounds = start >= x && x > end
  }

Remember, conditional branches are relatively slow, and we want to avoid them where we can. If this is, for instance, the test of a loop, the extra branch is not a good thing.

Option 3 avoids this issue, but it requires more space. It also requires branches in other places which care about order.

And both 2 and 3 complicate slicing. If you say `array[6...0] = newValues`, the operation should probably be performed such that `array[6] == newValues[0]`. That seems difficult to implement—and all too easy to forget to handle. And I can imagine that it might prevent certain optimizations if the compiler couldn't prove they were forward ranges.

···

--
Brent Royal-Gordon
Architechies

Exactly. Up until just a few days ago, I would’ve agreed with Wallacy. I thought the range operators, “a … b” and “a ..< b”, were supposed to represent “[a b]” and “[a b)”. Once the topic came up here, I realized that they were intended to approximate the notation “a ≤ x ≤ b” and “a ≤ x < b”, which visually speaking makes a lot more sense to me. With that in mind, “a <.. b” and “a <.< b” are the correct ways to extend the notation. IMHO, anyway.

- Dave Sweeris

···

On Apr 9, 2016, at 3:27 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:
On Sat, Apr 9, 2016 at 5:44 AM, Wallacy via swift-evolution > <swift-evolution@swift.org> wrote:

Just as note, i think the sintax should be:

0...9
0..<9
0>..9
0>.<9

Because the intention is produce a number bigger than 0 (start). So greater
than zero less than nine.

That's not typically how it's written in math. When x is between two
values a and b, it's written a < x < b. The pointy end of the symbol
faces the smaller value. Here, 0 is the smaller value, so the pointy
end must face it, as in `0 <.. 9` and `0 <.< 9`.

The sign of the stride size is already (in the current version of Swift)
evaluated in the stride generator/iterator; as written, it is evaluated
every time next() is called, but of course that is optimized away when
possible. What we are proposing adds no additional logic to the loop. I
can't see a reason why inserting a line to trap on a negative stride size
would show any measurable improvement in performance compared to inserting
a line to use upperBound for start and lowerBound for end instead of vice
versa when initializing an instance of StrideTo/StrideThrough. This would
not be a branch in StrideToIterator.next(), nor even in
StrideToIterator.init(), but rather in StrideTo.init() or even
striding(by:). Rest assured that degrading performance for the most common
use case would be unacceptable (at least to me).

It is already the case that your code needs to account for the sign of the
stride size. Currently, not doing so runs the risk of unintentionally
creating an empty sequence. In our proposal, the result of using a stride
size of unintended sign would differ but the fact that you need to account
for it is unchanged. We are not proposing trapping on negative stride sizes
nor allowing ranges such as 9...0.

···

On Sat, Apr 9, 2016 at 4:53 PM Michel Fortin <michel.fortin@michelf.ca> wrote:

Le 9 avr. 2016 à 4:25, Xiaodi Wu <xiaodi.wu@gmail.com> a écrit :
> Note that it's not possible to write 9...0. Once that is clear, it's
> fairly intuitive (IMO) to look to the stride size for indicating the
> direction in which we stride, since that is the only other value there
> is.

I know it's not possible to write 9...0. But I disagree that it's
intuitive that a negative step value would reverse the start and stop
values. I think to be consistent it should trap, like with a step of zero,
because you can't go from 0 to 9 with a negative step.

It's especially important to realize that if the step value is a variable
you will not be able to know which is the start value and which is the stop
criterion by quickly inspecting the code because the sign of the step is
hidden away in the value of that variable.

In other words, whether the loop begins at the start or the end of the
range becomes a runtime decision. There is a branch in the generator that
swaps the start and end values depending on that sign. If the step value is
not a literal constant, inlining might not be able to elide that branch.
Add another branch to make it trap when given a step of zero. And if it
traps when the range is invalid (such as in 9...0) then that's another
branch to check this when you form the range. That's 3 branches needed
before even starting the loop.

Perhaps I'm just complaining about loosing the good performance
characteristics of a C-style for loop. In the debate about this it was
stated many times that `stride` could be used and would have the same
performance. Is that promise about to be broken now?

On a final note: it's common, for me at least, to enter a loop with the
start position already higher than the stop criterion, essentially doing
something like `stride(from: 10, to 5, by 1)` which results in the loop
being skipped. Translated into this new syntax, it'd become something like
`(10...5).striding(by: 1)` which of course will trap because of the invalid
range, thus necessitating an extra `if` to check for this. That clutters
the code and adds even more branches. Perhaps the solution to this is to
not trap on ranges with a negative length, although that's probably going
to cause other headaches elsewhere.

--
Michel Fortin
https://michelf.ca

It’s not a matter of floating point error accumulation… At least on my machine, once a Double hits +/-∞, there’s no way that I know of to get back to normal floating point numbers. That is to say, for *all* normal, finite values of x, "-Double.infinity + x" will just return “-inf". If x is to equal Double.infinity, Double.NaN, or Double.quietNaN, then it’ll return “nan” (which, incidentally, will fail the regular equality test… Double.NaN isn’t even equal to itself; I think checking the floating point class is the way to do it).

I could easily be missing something, but AFAICT the only way to always get the correct sequence (without splitting the floating point types off into their own thing) is either have a negative stride swap start and end *before* the StrideTo starts generating values (that is, *not* by just calling `.reverse()` on something with a positive stride), or to allow “0 ..< -Double.infinity” to be a valid range (with the negative stride being implied).

- Dave Sweeris

···

On Apr 9, 2016, at 6:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Yikes. Not too concerned about the infinite loop issue, as floating point strides when fixed to avoid error accumulation will necessarily enforce a finite number of steps. However, you're talking a regular, not-at-all-lazy Array being returned? That would be not good at all...

On Sun, Apr 10, 2016 at 12:29 AM Dave via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Apr 9, 2016, at 4:33 AM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

While I’m in favour of the basic idea I think the operator selection is too complex, and I’m not sure about the need for negative strides. Really all I want are the following:

  (0 ... 6).striding(by: 2) // [0, 2, 4, 6] x from 0 to 6
  (0 ..< 6).striding(by: 2) // [0, 2, 4] x from 0 while <6
  (6 ... 0).striding(by: 2) // [6, 4, 2, 0] x from 6 to 0
  (6 ..> 0).striding(by: 2) // [6, 4, 2] x from 6 while >0

Everything else should be coverable either by flipping the order, or using .reverse(). The main advantage is that there’s only one new operator to clarify the 6 ..> 0 case, though you could always just reuse the existing operator if you just interpret it as “x from 6 to, but not including, 0"

`.reverse()` returns an array, though, not a StrideTo<>, which means it’ll get in an infinite loop on infinite sequences. This works fine:
for i in stride(from: 0.0, to: Double.infinity, by: M_PI) {
    if someTestInvolving(i) { break }
    ...
}

But this never even starts executing the loop because of the infinite loop inside `.reverse()`:
for i in stride(from: -Double.infinity, to: 0.0, by: M_PI).reverse() {
    if someTestInvolving(i) { break }
    ...
}

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Oh, right, we’re talking about Swift 3.0… I really need to get that up and running on my computer.

···

On Apr 9, 2016, at 8:58 PM, Dave Abrahams <dabrahams@apple.com> wrote:

on Sat Apr 09 2016, davesweeris-AT-mac.com wrote:

   On Apr 9, 2016, at 4:33 AM, Haravikk via swift-evolution >> <swift-evolution@swift.org> wrote:

   While I’m in favour of the basic idea I think the operator selection is too
   complex, and I’m not sure about the need for negative strides. Really all I
   want are the following:

   (0 ... 6).striding(by: 2) // [0, 2, 4, 6] x from 0 to 6
   (0 ..< 6).striding(by: 2) // [0, 2, 4] x from 0 while <6
   (6 ... 0).striding(by: 2) // [6, 4, 2, 0] x from 6 to 0
   (6 ..> 0).striding(by: 2) // [6, 4, 2] x from 6 while >0

   Everything else should be coverable either by flipping the order, or using .
   reverse(). The main advantage is that there’s only one new operator to
   clarify the 6 ..> 0 case, though you could always just reuse the existing
   operator if you just interpret it as “x from 6 to, but not including, 0"

`.reverse()` returns an array, though, not a StrideTo<>,

.reversed() returns a ReversedCollection when the underlying collection
is bidirectional:

https://github.com/apple/swift/blob/swift-3-indexing-model/stdlib/public/core/Reverse.swift#L250

That's lazy and cheap.

which means it’ll get in an infinite loop on infinite sequences.
This works fine: for i in stride(from: 0.0, to: Double.infinity, by:
M_PI) { if someTestInvolving(i) { break } ... }

But this never even starts executing the loop because of the infinite loop
inside `.reverse()`:
for i in stride(from: -Double.infinity, to: 0.0, by: M_PI).reverse() {
if someTestInvolving(i) { break }
...
}

- Dave Sweeris

--
Dave

We will be proposing exactly that which you've put in parentheses, i.e.
floating point types will get their own strides, and it will be a
precondition failure to try to stride from or to infinity or nan :)

···

On Sun, Apr 10, 2016 at 4:47 AM <davesweeris@mac.com> wrote:

It’s not a matter of floating point error accumulation… At least on my
machine, once a Double hits +/-∞, there’s no way that I know of to get back
to normal floating point numbers. That is to say, for *all* normal, finite
values of x, "-Double.infinity + x" will just return “-inf". If x is to
equal Double.infinity, Double.NaN, or Double.quietNaN, then it’ll return
“nan” (which, incidentally, will fail the regular equality test… Double.NaN
isn’t even equal to itself; I think checking the floating point class is
the way to do it).

I could easily be missing something, but AFAICT the only way to always get
the correct sequence (without splitting the floating point types off into
their own thing) is either have a negative stride swap start and end
*before* the StrideTo starts generating values (that is, *not* by just
calling `.reverse()` on something with a positive stride), or to allow “0
..< -Double.infinity” to be a valid range (with the negative stride being
implied).

- Dave Sweeris

On Apr 9, 2016, at 6:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Yikes. Not too concerned about the infinite loop issue, as floating point
strides when fixed to avoid error accumulation will necessarily enforce a
finite number of steps. However, you're talking a regular, not-at-all-lazy
Array being returned? That would be not good at all...

On Sun, Apr 10, 2016 at 12:29 AM Dave via swift-evolution < > swift-evolution@swift.org> wrote:

On Apr 9, 2016, at 4:33 AM, Haravikk via swift-evolution < >> swift-evolution@swift.org> wrote:

While I’m in favour of the basic idea I think the operator selection is
too complex, and I’m not sure about the need for negative strides. Really
all I want are the following:

(0 ... 6).striding(by: 2) // [0, 2, 4, 6] x from 0 to 6
(0 ..< 6).striding(by: 2) // [0, 2, 4] x from 0 while <6
(6 ... 0).striding(by: 2) // [6, 4, 2, 0] x from 6 to 0
(6 ..> 0).striding(by: 2) // [6, 4, 2] x from 6 while >0

Everything else should be coverable either by flipping the order, or
using .reverse(). The main advantage is that there’s only one new operator
to clarify the 6 ..> 0 case, though you could always just reuse the
existing operator if you just interpret it as “x from 6 to, but not
including, 0"

`.reverse()` returns an array, though, not a StrideTo<>, which means
it’ll get in an infinite loop on infinite sequences. This works fine:
for i in stride(from: 0.0, to: Double.infinity, by: M_PI) {
    if someTestInvolving(i) { break }
    ...
}

But this never even starts executing the loop because of the infinite
loop inside `.reverse()`:
for i in stride(from: -Double.infinity, to: 0.0, by: M_PI).reverse() {
    if someTestInvolving(i) { break }
    ...
}

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Perhaps it's a bit extreme, but my idea for stride is that it should only have one branch in the loop condition and absolutely no other branch. The primitive stride could be defined like this:

  stride(from: 0, compare: <, to: 10, by: 2)

and on top you could add some convenience functions for the <, >, <=, and >= cases:

  stride(from: 0, to: 10, by: 2) // stride(from: 0, compare: <, to: 10, by: 2)
  stride(from: 10, downTo: 0, by: -2) // stride(from: 10, compare: >, to: 0, by -2)

  stride(from: 0, through: 10, by: 2) // stride(from: 10, compare: <=, to: 10, by 2)
  stride(from: 10, downThrough: 0, by: -2) // stride(from: 10, compare: >=, to: 0, by -2)

None of these should try to prevent you from making an infinite sequence, so no trap for `by: 0` or one that moves the value in the wrong direction. And certainly no extra loop branch based on the sign of the stride (that's the most important part).

Based on that, you could add some convenience for ranges:

  (0 ..< 10).striding(by: 2) // shortcut for: stride(from: 0, to: 10, by 2)
  (0 ..< 10).stridingDown(by: -2) // shortcut for: stride(from: 10, to: 0, by -2)

I guess in this case it'd be fine to add a precondition to check that `by` is not in the reverse direction so we can preserve the semantics of iterating over a collection. But because of this precondition and the other one in Range, I would actually prefer the non-range forms if I was using a `stride` in a nested loop.

···

Le 10 avr. 2016 à 6:17, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

Remember, conditional branches are relatively slow, and we want to avoid them where we can. If this is, for instance, the test of a loop, the extra branch is not a good thing.

--
Michel Fortin
https://michelf.ca

IMO that is an acceptable result for collections that are not
bidirectional, just as in Swift 2 you can't measure the distance between
two ForwardIndex'es that are in the wrong order.

···

on Sun Apr 10 2016, Brent Royal-Gordon <swift-evolution@swift.org> wrote:

I can’t imagine any scenario in which getting the step's sign wrong wouldn’t just be a typo.

Well, it could be an error if you derive the stride by subtracting two
numbers, and the second number is unexpectedly larger than the
first. Of course, that makes it a *bug*, not a typo.

Why not just assign it the correct sign during the init function?
(0 ... 6).striding(by: 2) // [0, 2, 4, 6], end > start, so stride = by
(6 ... 0).striding(by: 2) // [6, 4, 2, 0], start > end, so stride = -by

One reason not to do it this way is that, if we extend `striding(by:)`
to other collections, they will not be as easy to walk backwards
through as this. You will have to do something like
`collection.reversed().striding(by:)` which will be a hassle.

--
Dave

Any thoughts on the alternative I mentioned a little earlier to define overloads instead of positive/negative? i.e- you would have two methods, .striding(forwardBy:) and .striding(backwardBy:). In addition to eliminating the use of a negative stride to indicate direction, this has the advantage that .striding(backwardBy:) can be defined only for types with a ReverseIndex or only for collections (as you can stride through a sequence, but only by going forward).

This should also make documentation a bit clearer, otherwise you’ve got the caveat that to go backwards requires a negative value, but only if the type supports that, which a developer would then need to check. Instead it either has the backwardBy variant or not.

I know that advance(by:) supports negative values, but this is actually something I wouldn’t mind seeing changed as well, as it has the same issues (passing a negative value in looks fine until you realise the type is a ForwardIndex only). It would also allow us to define Distance types that don’t support a direction, since this would be given by the choice of method called instead.

Of course I’d still like to be able to define 6 … 0 or whatever, but this would at least eliminate what I dislike about using negatives for direction.

···

On 10 Apr 2016, at 11:17, Brent Royal-Gordon <brent@architechies.com> wrote:

Why not just assign it the correct sign during the init function?
(0 ... 6).striding(by: 2) // [0, 2, 4, 6], end > start, so stride = by
(6 ... 0).striding(by: 2) // [6, 4, 2, 0], start > end, so stride = -by

One reason not to do it this way is that, if we extend `striding(by:)` to other collections, they will not be as easy to walk backwards through as this. You will have to do something like `collection.reversed().striding(by:)` which will be a hassle.

Just as note, i think the sintax should be:

0...9
0..<9
0>..9
0>.<9

I agree with this.

Because the intention is produce a number bigger than 0 (start). So greater
than zero less than nine.

That's not typically how it's written in math. When x is between two
values a and b, it's written a < x < b. The pointy end of the symbol
faces the smaller value. Here, 0 is the smaller value, so the pointy
end must face it, as in `0 <.. 9` and `0 <.< 9`.

I don't agree with this and I bet this is why Perl uses ^ for that purpose.

Including these not yet proposed operators will be distracting to the review of the proposal. These discussions would be better served as their own proposed proposal.
I recommend removing any mention of them from this proposal.

···

On Apr 9, 2016, at 1:27 AM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:
On Sat, Apr 9, 2016 at 5:44 AM, Wallacy via swift-evolution > <swift-evolution@swift.org> wrote:

Em sex, 8 de abr de 2016 22:50, Michel Fortin via swift-evolution >> <swift-evolution@swift.org> escreveu:

Le 8 avr. 2016 à 14:37, Erica Sadun via swift-evolution >>> <swift-evolution@swift.org> a écrit :

(0 ... 9).striding(by: -2) == [9, 7, 5, 3, 1]

The above reads wrong to me. The expression has to be read differently
depending on the tinny detail that is the sign of the step that comes last:

* positive step: from 0 to 9 striding by 2
* negative step: to 0 from 9 striding by -2

Am I the only one thinking it's a bit too clever to swap the start and
stop parts like this?

--
Michel Fortin
https://michelf.ca

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

The sign of the stride size is already (in the current version of Swift) evaluated in the stride generator/iterator; as written, it is evaluated every time next() is called, but of course that is optimized away when possible.

You're right. I had to check the code before I could believe it. How terrible. There's two branches per loop iteration:

I know the optimizer is probably going to be good enough to elide the first branch in most circumstances... but still, relying on the optimizer doing its magic does not seem very wise. It's not like you'll get a warning when that branch is not elided.

What we are proposing adds no additional logic to the loop. I can't see a reason why inserting a line to trap on a negative stride size would show any measurable improvement in performance compared to inserting a line to use upperBound for start and lowerBound for end instead of vice versa when initializing an instance of StrideTo/StrideThrough.

That suggestion was based on consistency more than performance. The first part of my last email was *not* about performance. I was saying that semantically it would make more sense to me if it did the same thing as zero. "I think to be consistent it should trap, like with a step of zero, because you can't go from 0 to 9 with a negative step." I guess we can agree to disagree on this.

Performance-wise, it would actually be one less branch at the start of the loop since you can fold it with the zero case using `predcondition(stride > 0)`. And I now realize that it'd make it possible to eliminate that dubious extra branch in `next()`, so that's one giant reason to do it.

I guess my main issue with `stride` as it is today is that it tries to be clever in the edge cases instead of just giving you an infinite sequence. Reversing the start and stop boundaries when the step is negative is just a way of making it even more clever. So perhaps it fits with the current direction, but it does not suit me.

···

Le 9 avr. 2016 à 13:23, Xiaodi Wu <xiaodi.wu@gmail.com> a écrit :

--
Michel Fortin
https://michelf.ca

I understand (and agree with) 3/4 of that… Why do we want to prevent striding *to* an infinity? I mean, yeah it’ll take a long time to get there, but with the new floating point stride code, a floating point value will *eventually* “overflow” into infinity (or `iteration += 1` will overflow and crash), it’s just that at that point there isn’t a straight-forward way to go the other direction anymore.

Actually, striding from an infinity should be ok, too, as long as it’s not the actual starting point:
let x = -Double.infinity ... 0.0 // Big Problems in, um, Non-Little Loops… or something… (and apologies to Kurt Russell)
let x = -Double.infinity <.. 0.0 // starts at `nextafter(start, end)` (-1.797693134862316e+308, in this case)

If the infinities are definitely out even as exclusive endpoints, can the floating point types get min/max properties like the integer types have?
let x = Double.min ... Double.max // same as -1.797693134862316e+308 ... 1.797693134862316e+308, but way easier to write
let x = Float.min ... Float.max // same as -3.402823e+38 ... 3.402823e+38

- Dave Sweeris

···

On Apr 10, 2016, at 12:05 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

We will be proposing exactly that which you've put in parentheses, i.e. floating point types will get their own strides, and it will be a precondition failure to try to stride from or to infinity or nan :)

On Sun, Apr 10, 2016 at 4:47 AM <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:
It’s not a matter of floating point error accumulation… At least on my machine, once a Double hits +/-∞, there’s no way that I know of to get back to normal floating point numbers. That is to say, for *all* normal, finite values of x, "-Double.infinity + x" will just return “-inf". If x is to equal Double.infinity, Double.NaN, or Double.quietNaN, then it’ll return “nan” (which, incidentally, will fail the regular equality test… Double.NaN isn’t even equal to itself; I think checking the floating point class is the way to do it).

I could easily be missing something, but AFAICT the only way to always get the correct sequence (without splitting the floating point types off into their own thing) is either have a negative stride swap start and end *before* the StrideTo starts generating values (that is, *not* by just calling `.reverse()` on something with a positive stride), or to allow “0 ..< -Double.infinity” to be a valid range (with the negative stride being implied).

- Dave Sweeris

On Apr 9, 2016, at 6:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Yikes. Not too concerned about the infinite loop issue, as floating point strides when fixed to avoid error accumulation will necessarily enforce a finite number of steps. However, you're talking a regular, not-at-all-lazy Array being returned? That would be not good at all...

On Sun, Apr 10, 2016 at 12:29 AM Dave via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Apr 9, 2016, at 4:33 AM, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

While I’m in favour of the basic idea I think the operator selection is too complex, and I’m not sure about the need for negative strides. Really all I want are the following:

  (0 ... 6).striding(by: 2) // [0, 2, 4, 6] x from 0 to 6
  (0 ..< 6).striding(by: 2) // [0, 2, 4] x from 0 while <6
  (6 ... 0).striding(by: 2) // [6, 4, 2, 0] x from 6 to 0
  (6 ..> 0).striding(by: 2) // [6, 4, 2] x from 6 while >0

Everything else should be coverable either by flipping the order, or using .reverse(). The main advantage is that there’s only one new operator to clarify the 6 ..> 0 case, though you could always just reuse the existing operator if you just interpret it as “x from 6 to, but not including, 0"

`.reverse()` returns an array, though, not a StrideTo<>, which means it’ll get in an infinite loop on infinite sequences. This works fine:
for i in stride(from: 0.0, to: Double.infinity, by: M_PI) {
    if someTestInvolving(i) { break }
    ...
}

But this never even starts executing the loop because of the infinite loop inside `.reverse()`:
for i in stride(from: -Double.infinity, to: 0.0, by: M_PI).reverse() {
    if someTestInvolving(i) { break }
    ...
}

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

The iteration counter will be floating point and the iterator will break
down after incrementing by one is no longer exactly representable. It has
been made clear on this list that there is a strong preference that no
floating point stride should degenerate into an infinite loop due to this
issue. Thus, there will be a limit at the time a floating point stride is
created of 2^53 steps (in the case of a Double--some thought is necessary
for a Float). As currently mocked up the logic is whether start + 2^53 *
stride gets you to/through the end. If not, precondition failure.

···

On Sun, Apr 10, 2016 at 7:41 AM <davesweeris@mac.com> wrote:

I understand (and agree with) 3/4 of that… Why do we want to prevent
striding *to* an infinity? I mean, yeah it’ll take a long time to get
there, but with the new floating point stride code, a floating point value
will *eventually* “overflow” into infinity (or `iteration += 1` will
overflow and crash), it’s just that at that point there isn’t a
straight-forward way to go the other direction anymore.

Actually, striding from an infinity should be ok, too, as long as it’s not
the actual starting point:
let x = -Double.infinity ... 0.0 // Big Problems in, um, Non-Little
Loops… or something… (and apologies to Kurt Russell)
let x = -Double.infinity <.. 0.0 // starts at `nextafter(start, end)`
(-1.797693134862316e+308, in this case)

If the infinities are definitely out even as exclusive endpoints, can the
floating point types get min/max properties like the integer types have?
let x = Double.min ... Double.max // same as -1.797693134862316e+308 ... 1.797693134862316e+308,
but way easier to write
let x = Float.min ... Float.max // same as -3.402823e+38 ... 3.402823e+38

- Dave Sweeris

On Apr 10, 2016, at 12:05 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

We will be proposing exactly that which you've put in parentheses, i.e.
floating point types will get their own strides, and it will be a
precondition failure to try to stride from or to infinity or nan :)

On Sun, Apr 10, 2016 at 4:47 AM <davesweeris@mac.com> wrote:

It’s not a matter of floating point error accumulation… At least on my
machine, once a Double hits +/-∞, there’s no way that I know of to get back
to normal floating point numbers. That is to say, for *all* normal, finite
values of x, "-Double.infinity + x" will just return “-inf". If x is to
equal Double.infinity, Double.NaN, or Double.quietNaN, then it’ll return
“nan” (which, incidentally, will fail the regular equality test… Double.NaN
isn’t even equal to itself; I think checking the floating point class is
the way to do it).

I could easily be missing something, but AFAICT the only way to always
get the correct sequence (without splitting the floating point types off
into their own thing) is either have a negative stride swap start and end
*before* the StrideTo starts generating values (that is, *not* by just
calling `.reverse()` on something with a positive stride), or to allow “0
..< -Double.infinity” to be a valid range (with the negative stride being
implied).

- Dave Sweeris

On Apr 9, 2016, at 6:59 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Yikes. Not too concerned about the infinite loop issue, as floating point
strides when fixed to avoid error accumulation will necessarily enforce a
finite number of steps. However, you're talking a regular, not-at-all-lazy
Array being returned? That would be not good at all...

On Sun, Apr 10, 2016 at 12:29 AM Dave via swift-evolution < >> swift-evolution@swift.org> wrote:

On Apr 9, 2016, at 4:33 AM, Haravikk via swift-evolution < >>> swift-evolution@swift.org> wrote:

While I’m in favour of the basic idea I think the operator selection is
too complex, and I’m not sure about the need for negative strides. Really
all I want are the following:

(0 ... 6).striding(by: 2) // [0, 2, 4, 6] x from 0 to 6
(0 ..< 6).striding(by: 2) // [0, 2, 4] x from 0 while <6
(6 ... 0).striding(by: 2) // [6, 4, 2, 0] x from 6 to 0
(6 ..> 0).striding(by: 2) // [6, 4, 2] x from 6 while >0

Everything else should be coverable either by flipping the order, or
using .reverse(). The main advantage is that there’s only one new operator
to clarify the 6 ..> 0 case, though you could always just reuse the
existing operator if you just interpret it as “x from 6 to, but not
including, 0"

`.reverse()` returns an array, though, not a StrideTo<>, which means
it’ll get in an infinite loop on infinite sequences. This works fine:
for i in stride(from: 0.0, to: Double.infinity, by: M_PI) {
    if someTestInvolving(i) { break }
    ...
}

But this never even starts executing the loop because of the infinite
loop inside `.reverse()`:
for i in stride(from: -Double.infinity, to: 0.0, by: M_PI).reverse() {
    if someTestInvolving(i) { break }
    ...
}

- Dave Sweeris
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Remember, conditional branches are relatively slow, and we want to

avoid them where we can. If this is, for instance, the test of a loop,
the extra branch is not a good thing.

Perhaps it's a bit extreme, but my idea for stride is that it should
only have one branch in the loop condition and absolutely no other
branch.

I agree that would be ideal. When the stride is statically known, I am
confident that the optimizer can eliminate the branch. When the stride
is not statically known, presumably you need the branch anyway, though
one might have to hope for the optimizer to copy and split the loop
(i.e. create two specializations based on sign).

The primitive stride could be defined like this:

  stride(from: 0, compare: <, to: 10, by: 2)

Now you're adding a function call in lieu of a branch. We could easily
build stride so it stores a comparison function based on the sign of the
stride, if we thought this would be better. Would it?

···

on Sun Apr 10 2016, Michel Fortin <swift-evolution@swift.org> wrote:

Le 10 avr. 2016 à 6:17, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

and on top you could add some convenience functions for the <, >, <=, and >= cases:

  stride(from: 0, to: 10, by: 2) // stride(from: 0, compare: <, to: 10, by: 2)
  stride(from: 10, downTo: 0, by: -2) // stride(from: 10, compare: >, to: 0, by -2)

  stride(from: 0, through: 10, by: 2) // stride(from: 10, compare: <=, to: 10, by 2)
  stride(from: 10, downThrough: 0, by: -2) // stride(from: 10, compare: >=, to: 0, by -2)

None of these should try to prevent you from making an infinite
sequence, so no trap for `by: 0` or one that moves the value in the
wrong direction. And certainly no extra loop branch based on the sign
of the stride (that's the most important part).

Based on that, you could add some convenience for ranges:

  (0 ..< 10).striding(by: 2) // shortcut for: stride(from: 0, to: 10, by 2)
  (0 ..< 10).stridingDown(by: -2) // shortcut for: stride(from: 10, to: 0, by -2)

I guess in this case it'd be fine to add a precondition to check that
`by` is not in the reverse direction so we can preserve the semantics
of iterating over a collection. But because of this precondition and
the other one in Range, I would actually prefer the non-range forms if
I was using a `stride` in a nested loop.

--
Dave

What types do you have in mind that would only support positive distances?
All numeric types (yes, even UInt, etc.) have signed distances, which
reflects the basic mathematical abstraction of a number line.

A consistent behavior with signed distances is so important that we are
currently struggling with an interesting issue with floating point types,
which is that due to rounding error 10.0 + a - a != 10.0 for some values of
a.

···

On Sun, Apr 10, 2016 at 12:53 PM Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

On 10 Apr 2016, at 11:17, Brent Royal-Gordon <brent@architechies.com> > wrote:

Why not just assign it the correct sign during the init function?
(0 ... 6).striding(by: 2) // [0, 2, 4, 6], end > start, so stride = by
(6 ... 0).striding(by: 2) // [6, 4, 2, 0], start > end, so stride = -by

One reason not to do it this way is that, if we extend `striding(by:)` to
other collections, they will not be as easy to walk backwards through as
this. You will have to do something like
`collection.reversed().striding(by:)` which will be a hassle.

Any thoughts on the alternative I mentioned a little earlier to define
overloads instead of positive/negative? i.e- you would have two methods,
.striding(forwardBy:) and .striding(backwardBy:). In addition to
eliminating the use of a negative stride to indicate direction, this has
the advantage that .striding(backwardBy:) can be defined only for types
with a ReverseIndex or only for collections (as you can stride through a
sequence, but only by going forward).

This should also make documentation a bit clearer, otherwise you’ve got
the caveat that to go backwards requires a negative value, but only if the
type supports that, which a developer would then need to check. Instead it
either has the backwardBy variant or not.

I know that advance(by:) supports negative values, but this is actually
something I wouldn’t mind seeing changed as well, as it has the same issues
(passing a negative value in looks fine until you realise the type is a
ForwardIndex only). It would also allow us to define Distance types that
don’t support a direction, since this would be given by the choice of
method called instead.

Of course I’d still like to be able to define 6 … 0 or whatever, but this
would at least eliminate what I dislike about using negatives for direction.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

In the best case scenario the optimiser inlines everything and you have a straightforward loop with no indirect jump through a function pointer. We're already counting on most of the `stride` mechanics being inlined anyway, so I don't think it's a stretch to assume inlining will work correctly for this. To improve likelihood of inlining, don't make that function depend on a runtime value such as the sign of the stride.

But there's no guaranty with the optimizer. In the worse case we store a function pointer on the stack and do an indirect jump to that function at every loop iteration to do the comparison. I think that's better than adding a branch at every iteration, but that could depend on the CPU cache and branch prediction and various other factors (code locality, etc.). I won't try to predict if the worse case is worse or better than with what we have now since I'm not even sure how to benchmark this.

But let's go back to the current implementation for a minute.

`stride` should work with any integer type. That should include custom ones such as an implementation of BigInt. If for some reason the optimizer cannot inline the `stride < 0` comparison in the `next()` function (where stride is of the generic type Element), it will not be able to deduce that the result is always the same (there's no `pure` annotation in the language). So it will pessimistically evaluate `stride < 0` at every iteration. In itself that comparison could be costly if we're talking about BigInt. And to that you add the cost of branching on the result of that comparison.

So if you take this into account, storing the comparator as part of the stride makes the cost more predictable: not only there is one branch less in `next()`, but you avoid evaluating the condition which has an unknown cost: the `stride < 0` part.

And ideally you should make sure the optimizer can replace the indirect call with a direct call to the comparator. You do that by not making the comparator choice dependent on a runtime value, hence why I suggested having distinct "down" variants for the convenience functions: `stride(from:downTo:by:)` and `stride(from:downThrough:by:)` and `Range.strindingDown(by:)`.

···

Le 10 avr. 2016 à 13:06, Dave Abrahams via swift-evolution <swift-evolution@swift.org> a écrit :

on Sun Apr 10 2016, Michel Fortin <swift-evolution@swift.org> wrote:

Le 10 avr. 2016 à 6:17, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

Remember, conditional branches are relatively slow, and we want to

avoid them where we can. If this is, for instance, the test of a loop,
the extra branch is not a good thing.

Perhaps it's a bit extreme, but my idea for stride is that it should
only have one branch in the loop condition and absolutely no other
branch.

I agree that would be ideal. When the stride is statically known, I am
confident that the optimizer can eliminate the branch. When the stride
is not statically known, presumably you need the branch anyway, though
one might have to hope for the optimizer to copy and split the loop
(i.e. create two specializations based on sign).

The primitive stride could be defined like this:

  stride(from: 0, compare: <, to: 10, by: 2)

Now you're adding a function call in lieu of a branch. We could easily
build stride so it stores a comparison function based on the sign of the
stride, if we thought this would be better. Would it?

--
Michel Fortin
https://michelf.ca

What types do you have in mind that would only support positive distances? All numeric types (yes, even UInt, etc.) have signed distances, which reflects the basic mathematical abstraction of a number line.

Say you wanted to stride through a singly-linked list, it would actually be beneficial to support only forward strides, the same is true of sequences, as you either may not know what the endpoint is, or would have to step through the whole sequence to find it (plus buffer every value in order to do-so safely).

A consistent behavior with signed distances is so important that we are currently struggling with an interesting issue with floating point types, which is that due to rounding error 10.0 + a - a != 10.0 for some values of a.

While that’s interesting I’m not sure why the sign is important; to me a stride is a width so it being negative makes no sense. For example, say I laid an array of Ints, organised into groups of five (and also that I’m lunatic who won’t use a tuple for this), the stride of this array is 5 whether I’m stepping through it forwards or backwards. Imagine I defined this like so (more realistically it’d be a struct or a class):

  typealias StridedIntegerArray:(stride:Int, array:[Int])

If the stride is set to 5, it’s always 5, the only thing that changes is whether I want to stride from the start or end of the array, plus I could things like:

  myStridedIntegerArray.prefix(from: 2).striding(forwardBy: myStridedIntegerArray.stride) // Returns element at index 2, 7, 12, etc.

It just occurred to me that perhaps you intended this method only for ranges specifically and that perhaps I’m confusing things, but it seems to me like it should be a method for all sequences (with reverse stride available on collections with a reverse index type) returning a generator that only returns (or computes) every Nth element, for generic sequences/collections this would take the start or end index and use advanced(by:), though again, I kind of feel like that should be two separate methods as well, but that’s for another issue I think.

···

On 10 Apr 2016, at 14:25, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Apr 10, 2016 at 12:53 PM Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 10 Apr 2016, at 11:17, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

Why not just assign it the correct sign during the init function?
(0 ... 6).striding(by: 2) // [0, 2, 4, 6], end > start, so stride = by
(6 ... 0).striding(by: 2) // [6, 4, 2, 0], start > end, so stride = -by

One reason not to do it this way is that, if we extend `striding(by:)` to other collections, they will not be as easy to walk backwards through as this. You will have to do something like `collection.reversed().striding(by:)` which will be a hassle.

Any thoughts on the alternative I mentioned a little earlier to define overloads instead of positive/negative? i.e- you would have two methods, .striding(forwardBy:) and .striding(backwardBy:). In addition to eliminating the use of a negative stride to indicate direction, this has the advantage that .striding(backwardBy:) can be defined only for types with a ReverseIndex or only for collections (as you can stride through a sequence, but only by going forward).

This should also make documentation a bit clearer, otherwise you’ve got the caveat that to go backwards requires a negative value, but only if the type supports that, which a developer would then need to check. Instead it either has the backwardBy variant or not.

I know that advance(by:) supports negative values, but this is actually something I wouldn’t mind seeing changed as well, as it has the same issues (passing a negative value in looks fine until you realise the type is a ForwardIndex only). It would also allow us to define Distance types that don’t support a direction, since this would be given by the choice of method called instead.

Of course I’d still like to be able to define 6 … 0 or whatever, but this would at least eliminate what I dislike about using negatives for direction.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

What types do you have in mind that would only support positive distances?
All numeric types (yes, even UInt, etc.) have signed distances, which
reflects the basic mathematical abstraction of a number line.

Say you wanted to stride through a singly-linked list, it would actually be
beneficial to support only forward strides, the same is true of sequences,
as you either may not know what the endpoint is, or would have to step
through the whole sequence to find it (plus buffer every value in order to
do-so safely).

A consistent behavior with signed distances is so important that we are
currently struggling with an interesting issue with floating point types,
which is that due to rounding error 10.0 + a - a != 10.0 for some values of
a.

While that’s interesting I’m not sure why the sign is important; to me a
stride is a width so it being negative makes no sense. For example, say I
laid an array of Ints, organised into groups of five (and also that I’m
lunatic who won’t use a tuple for this), the stride of this array is 5
whether I’m stepping through it forwards or backwards. Imagine I defined
this like so (more realistically it’d be a struct or a class):

typealias StridedIntegerArray:(stride:Int, array:[Int])

If the stride is set to 5, it’s always 5, the only thing that changes is
whether I want to stride from the start or end of the array, plus I could
things like:

myStridedIntegerArray.prefix(from: 2).striding(forwardBy:
myStridedIntegerArray.stride) // Returns element at index 2, 7, 12, etc.

When you have a sequence returning elements at index 12, 7, 2, etc.,
wouldn't you call the stride size -5? I would, because 12 + (-5) = 7.

It just occurred to me that perhaps you intended this method only for ranges
specifically and that perhaps I’m confusing things, but it seems to me like
it should be a method for all sequences (with reverse stride available on
collections with a reverse index type) returning a generator that only
returns (or computes) every Nth element, for generic sequences/collections
this would take the start or end index and use advanced(by:), though again,
I kind of feel like that should be two separate methods as well, but that’s
for another issue I think.

I don't think it should be for ranges only, but ranges are the extent
of this proposal.

That said, my own opinion is that striding should not be available on
sequences but on collections only. In their most commonly used form,
integer strides take a start and end, and there is a finite number of
things to stride over; thus, in my reasoning, strides can be extended
to cover anything else that has a known start and end and has a finite
number of things, which is guaranteed by conformance to Collection but
not to Sequence. (At the moment, StrideTo/Through conforms to Sequence
and not to Collection, but that is considered something to be fixed
and we will see if we can address that as part of this set of stride
overhauls.)

As I see it, we agree on the problem: the current algorithm cannot
accommodate singly linked lists and sequences because those things do
not have a known endpoint if you begin an attempt to stride. However,
my conclusion is the opposite of yours: namely, that they should not
have stride. Maybe they should have something similar, but it
shouldn't be stride.

···

On Sun, Apr 10, 2016 at 3:58 PM, Haravikk <swift-evolution@haravikk.me> wrote:

On 10 Apr 2016, at 14:25, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sun, Apr 10, 2016 at 12:53 PM Haravikk via swift-evolution > <swift-evolution@swift.org> wrote:

On 10 Apr 2016, at 11:17, Brent Royal-Gordon <brent@architechies.com> >> wrote:

Why not just assign it the correct sign during the init function?
(0 ... 6).striding(by: 2) // [0, 2, 4, 6], end > start, so stride = by
(6 ... 0).striding(by: 2) // [6, 4, 2, 0], start > end, so stride = -by

One reason not to do it this way is that, if we extend `striding(by:)` to
other collections, they will not be as easy to walk backwards through as
this. You will have to do something like
`collection.reversed().striding(by:)` which will be a hassle.

Any thoughts on the alternative I mentioned a little earlier to define
overloads instead of positive/negative? i.e- you would have two methods,
.striding(forwardBy:) and .striding(backwardBy:). In addition to eliminating
the use of a negative stride to indicate direction, this has the advantage
that .striding(backwardBy:) can be defined only for types with a
ReverseIndex or only for collections (as you can stride through a sequence,
but only by going forward).

This should also make documentation a bit clearer, otherwise you’ve got
the caveat that to go backwards requires a negative value, but only if the
type supports that, which a developer would then need to check. Instead it
either has the backwardBy variant or not.

I know that advance(by:) supports negative values, but this is actually
something I wouldn’t mind seeing changed as well, as it has the same issues
(passing a negative value in looks fine until you realise the type is a
ForwardIndex only). It would also allow us to define Distance types that
don’t support a direction, since this would be given by the choice of method
called instead.

Of course I’d still like to be able to define 6 … 0 or whatever, but this
would at least eliminate what I dislike about using negatives for direction.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

So if you take this into account, storing the comparator as part of
the stride makes the cost more predictable: not only there is one
branch less in `next()`, but you avoid evaluating the condition which
has an unknown cost: the `stride < 0` part.

And ideally you should make sure the optimizer can replace the
indirect call with a direct call to the comparator. You do that by not
making the comparator choice dependent on a runtime value, hence why I
suggested having distinct "down" variants for the convenience
functions: `stride(from:downTo:by:)` and
`stride(from:downThrough:by:)` and `Range.strindingDown(by:)`.

A few points:

1. There's a balance to be struck between making sure the optimizer can
   do a good job under *absolutely all circumstances*, and keeping the
   API surface area small and simple. That makes me somewhat reluctant
   to add “down” variants, if as I suspect the optimizer does well in
   the vast majority of cases without them.

2. Similarly, I view r.striding(by: x) as redundant with stride(from: x,

3. The fact that we're migrating C-style for loops to
   uses of stride, as noted in Stride should allow zero size. by danielgindi · Pull Request #2125 · apple/swift · GitHub,
   has convinced me that, sadly, we may need an answer that doesn't
   involve ranges. But maybe something like

     for x in loop(from: 0.1, while: { $0 < 10 }, next: { $0 + .2 })

   is sufficient for this purpose.

···

on Sun Apr 10 2016, Michel Fortin <michel.fortin-AT-michelf.ca> wrote:
   to: y, by: z). I'd rather not have them both.

--
Dave