[Discussion] stride behavior and a little bit of a call-back to digital numbers

It's so nice to see such care devoted to clarifying these existing
names. I agree with the premise that stride(to:by:) and
stride(through:by:) are poorly named, but I'd like to make two
critiques of this proposal--

Critique 1:

The basic distinction between the two current stride styles is that
one is inclusive and the other is exclusive of the end value. I agree
with you that "to" doesn't imply an exclusive end value, but "towards"
doesn't imply that the parameter is any sort of end value at
all--rather, it implies just a direction (or as you quote from the
NOAD, getting close or closer).

Two implications:

First, if I stride from 10 towards 0 by 1, by the plain English
meaning of the word "towards", I would expect to obtain 10, 9, 8, 7,
6, etc. If we simply rename stride(to:by:) to stride(towards:by:), I
would not get that result. By contrast, it makes sense from the
current name that stride(to:by:) attempts to increment using the `by`
parameter without considering whether the end value is greater than or
less than the start value; if you can't get from here "to" there by
such increments, too bad!

Do you want the stride to pick up the sign automatically? Instead of

print(Array(10.stride(to: 0, by: -1))) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], current Swift

have

print(Array(10.stride(to: 0, by: 1))) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], adjusted sign

Or am I missing your point of "if I stride from 10 towards 0 by 1"?

Second, if I stride from 0 towards 10 by 1 (in the English language,
not in Swift), I may or may not stop short of 10 itself. That is,
whether "towards" is inclusive or exclusive of the end value can't be
inferred from the meaning of the word; after all, if I'm making
strides towards a goal, I do intend to reach it, or at least that's
what I tell people when they ask how my PhD is going...

I'm searching for a word that means "in the direction of but never reaching".

print(Array(10.stride(to: 0, by: -1))) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1], current Swift

This never reaches 0.

Generalizing from the word "towards", I don't know that any two
prepositions in the English language can be used unambiguously to
convey the distinction between inclusive and exclusive end values.
Although, on some additional thought--if I had to suggest a
preposition, perhaps "until" or "till" would be more apt than
"towards".

`until` to me suggests reaching the end-point.

The saving grace of "to" and "through" in the current situation is
that the latter seems intuitively to go further than the former,

Quite often, it doesn't really:

print(Array(1.stride(to:10, by: 8))) // [1, 9]
print(Array(1.stride(through:10, by: 8))) // [1, 9]

and
if one deduces by analogy with the range operators that one of these
must exclude the end value and the other include it, then the two
names must mean what they do today. With three stride styles and three
prepositions, but only two range operators, this intuition is broken,
while the prepositions may not get much clearer (though I must admit
that your proposed use of "to" is an improvement).

Critique 2:

The original motivation behind your twin proposals was the epsilon
adjustment necessary for floating point end values. Your other
proposal fixes an issue with accumulated errors but doesn't solve the
need for an epsilon adjustment.

Actually, I've tweaked it this morning to take care of that. Reload: fpstride.md · GitHub

Here, you propose adding a third
stride style to solve that problem

It's really solving the 1/10/by 8 problem more than the 2.0 problem. Which is a big reason why I even
brought up the issue of separating the proposals in the first place. To me, the following just doesn't make
sense:

print(Array(1.stride(through:10, by: 8))) // [1, 9]

, along the way shuffling the naming
of the existing stride styles. Since you haven't presented other use
cases for that third stride style here, and you haven't listed
alternatives considered for solving the original motivating problem
(i.e. epsilon adjustment), let me propose one alternative:

Canonical use-cases for all three styles:

1 towards 5 by 1: [1, 2, 3, 4]
This style mimics a..<b but allows non-unit and negative progressions

1 to 5 by 1: [1, 2, 3, 4, 5]
This style mimics a...b but allows non-unit and negative progressions

1 through 10 by 8: [1, 9, 17]
This style ensures a non-unit and negative progression that passes to or through the final value,
ensuring that the range of the progression fully includes the range of the from and to values:
[first...last] subsumes [from...through]. You might call this a..>b

Keep the naming of stride styles as-is (inapt as they may be), and for
floating point end values make stride(through: aNumber, by: something)
equivalent to stride(to: theNextLargestRepresentableNumber, by:
somethingPositive) or stride(to: theNextSmallestRepresentableNumber,
by: somethingNegative). Would that solve your original issue
adequately?

Alternatively, if there are lots of examples that can be envisioned
for this third stride style, would the same examples suggest perhaps
that `..>` might be a useful third range operator?

I like that quite a lot actually.

···

On Mar 1, 2016, at 1:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

I'm adding in "upTo" as an alternative to "to". Reload in a minute or two.

-- E

···

On Mar 1, 2016, at 10:52 AM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Mon, Feb 29, 2016 at 5:14 PM, Erica Sadun via swift-evolution > <swift-evolution@swift.org> wrote:

Style 1: [start, end) by interval
This style is currently called to. I propose to rename it towards as each
value works towards end. The final value in the progression is less than end

The current usage of 'through' seems unambiguous to me. 'to' could be
improved by using 'upTo' instead.

stride(from: 1, upTo: 10, by: 1) // [1, 10)

'upTo' is already precedented in Collection.prefix(upTo:).

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

Please disregard previous reply. Just realized, what about 10..>1 by -1? upTo: doesn't work there.

···

On Mar 1, 2016, at 10:52 AM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Mon, Feb 29, 2016 at 5:14 PM, Erica Sadun via swift-evolution > <swift-evolution@swift.org> wrote:

Style 1: [start, end) by interval
This style is currently called to. I propose to rename it towards as each
value works towards end. The final value in the progression is less than end

The current usage of 'through' seems unambiguous to me. 'to' could be
improved by using 'upTo' instead.

stride(from: 1, upTo: 10, by: 1) // [1, 10)

'upTo' is already precedented in Collection.prefix(upTo:).

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

Your implementation of `fstride` is faulty, it doesn't produce the
`through` value. Suggest you change to:

public extension Double {

    public func doubleStride(through end: Double, by stride: Double) ->
LazyMapCollection<Range<Int>, Double> {

        let limit = Int(trunc((end - self) / stride))

        return (0 ... limit).lazy.map { self + Double($0) * stride }

    }

}

  -- Howard.

···

On 1 March 2016 at 12:16, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

On Feb 29, 2016, at 5:03 PM, Joe Groff <jgroff@apple.com> wrote:
I agree, splitting into two proposals is a good idea.

-Joe

Decoupling Floating Point Strides from Generic Implementations

   - Proposal: SE-00XX
   - Author(s): Erica Sadun <http://github.com/erica&gt;
   - Status: TBD
   - Review manager: TBD

Swift strides create progressions along "notionally continuous
one-dimensional values" using a series of offset values. This proposal
replaces the Swift's generic stride implementation with seperate algorithms
for integer strides (the current implementation) and floating point strides.

This proposal was discussed on-list in the "[Discussion] stride behavior
and a little bit of a call-back to digital numbers"
<http://article.gmane.org/gmane.comp.lang.swift.evolution/8014&gt;thread\.
<fpstride.md · GitHub

Strideable is genericized across both integer and floating point types. A
single implementation causes floating point strides to accumulate errors
through repeatedly adding by intervals. Floating point types deserve
their own floating point-aware implementation that minimizes errors.
<fpstride.md · GitHub
Art

A Strideable to sequence returns the sequence of values (self, self +
stride, self + stride + stride, ... *last*) where *last* is the last
value in the progression that is less than end.

A Strideable through sequence currently returns the sequence of values (
self, self + stride, self + tride + stride, ... *last*) where *last* is
the last value in the progression less than or equal to end. There is no
guarantee that end is an element of the sequence.

While floating point calls present an extremely common use-case, they use
integer-style math that accumulates errors during execution. Consider this
example:

let ideal = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]print(zip(Array(1.0.stride(through: 2.01, by: 0.1)), ideal).map(-))// prints [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16, // 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16, // 6.6613381477509392e-16, 6.6613381477509392e-16, 8.8817841970012523e-16, // 8.8817841970012523e-16]

   - To create an array containing values from 1.0 to 2.0, the developer
   must add an epsilon value to the throughargument. Otherwise the stride
   progression ends near 1.9. Increasing the argument from 2.0 to 2.01 is
   sufficient to include the end value.
   - The errors in the sequence increase over time. You see this as
   errors become larger towards the end of the progress. This is an artifact
   of the generic implementation.

<fpstride.md · GitHub
Design

Under the current implementation, each floating point addition in a
generic stride accrues errors. The following progression never reaches 2.0.

print(Array(1.0.stride(through: 2.0, by: 0.1)))
// Prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]

This same issue occurs with traditional C-style for loops. This is an
artifact of floating point math, and not the specific Swift statements:

var array: [Double] =
for var i = 1.0; i <= 2.0; i += 0.1 {
    array.append(i)
}
print("Array", array)
// Prints Array [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]

You should not have to manually add an epsilon to force a progression to
complete.

Floating point strides are inherently dissimilar to and should not be
genericized with integer strides. I propose separate their implementation,
freeing them to provide their own specialized progressions, using better
numeric methods. In doing so, floating point values are no longer tied to
implementations that unnecessarily accrue errors or otherwise provide
less-than-ideal solutions.

The following example provides a rough pass at what this might look like
for floating point math. I leave specific algorithm details to experts; a
decimal number solution would be more appropriate. The fun

See: RandomAscii's write-ups on all things floating point
<https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition&gt;
.

import Darwin
/// A `GeneratorType` for `DoubleStrideThrough`.public struct DoubleStrideThroughGenerator : GeneratorType {
    let start: Double
    let end: Double
    let stride: Double
    var iteration: Int = 0
    var done: Bool = false

    public init(start: Double, end: Double, stride: Double) {
        (self.start, self.end, self.stride) = (start, end, stride)
    }

    /// Advance to the next element and return it, or `nil` if no next
    /// element exists.
    public mutating func next() -> Double? {
        if done {
            return nil
        }
        let current = start + Double(iteration) * stride; iteration += 1
        if signbit(current - end) == signbit(stride) { // thanks Joe Groff
            if abs(current) > abs(end) {
                done = true
                return current
            }
            return nil
        }
        return current
    }
}
public struct DoubleStrideThrough : SequenceType {
    let start: Double
    let end: Double
    let stride: Double

    /// Return a *generator* over the elements of this *sequence*.
    ///
    /// - Complexity: O(1).
    public func generate() -> DoubleStrideThroughGenerator {
        return DoubleStrideThroughGenerator(
            start: start, end: end, stride: stride)
    }

    init(start: Double, end: Double, stride: Double) {
        _precondition(stride != 0, "stride size must not be zero")
        (self.start, self.end, self.stride) = (start, end, stride)
    }

}
public extension Double {
    public func fstride(
        through end: Double, by stride: Double
        ) -> DoubleStrideThrough {
        return DoubleStrideThrough(
            start: self, end: end, stride: stride)
    }
}

This implementation reduces floating point error by limiting accumulated
additions. It uses the current Swift 2.2 through semantics (versus the
revised through semantics proposed under separate cover), so it never
reaches 2.0 without adding an epsilon value.

print(Array(1.0.fstride(through: 2.0, by: 0.1)))// prints [1.0, 1.1000000000000001, 1.2, 1.3, 1.3999999999999999,// 1.5, 1.6000000000000001, 1.7000000000000002, 1.8, // 1.8999999999999999]
// versus the old styleprint(Array(1.0.stride(through: 2.0, by: 0.1)))// prints [1.0, 1.1000000000000001, 1.2000000000000002, 1.3000000000000003, // 1.4000000000000004, 1.5000000000000004, 1.6000000000000005, // 1.7000000000000006, 1.8000000000000007, 1.9000000000000008]
print(zip(Array(1.0.stride(through: 2.0, by: 0.1)),
          Array(1.0.fstride(through: 2.0, by: 0.1))).map(-))// prints [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16, // 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16, // 4.4408920985006262e-16, 6.6613381477509392e-16, 8.8817841970012523e-16]
let ideal = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]print(zip(Array(1.0.fstride(through: 2.0, by: 0.1)), ideal).map(-))print(zip(Array(1.0.stride(through: 2.0, by: 0.1)), ideal).map(-))
// prints// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.2204460492503131e-16, 0.0, 0.0]// [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16, // 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16, // 6.6613381477509392e-16, 6.6613381477509392e-16, 8.8817841970012523e-16]

If one were looking for a quick and dirty fix, the same kind of math used
in this rough solution (let value = start + count * interval) could be
adopted back into the current generic implementation.

<fpstride.md · GitHub
Considered
While precision math for decimal numbers would be better addressed by
introducing a decimal type and/or warnings for at-risk floating point
numbers, those features lie outside the scope of this proposal.

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

I was thinking about something similar, but as precise Strideable (direct increment ok) and impreciseStrideable (multiplicative must be used). But my main question is reversed, beside Double what other type/concept would require the multiplicative approached?

Dany

···

Le 1 mars 2016 à 05:56, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> a écrit :

If one were looking for a quick and dirty fix, the same kind of math used in this rough solution (let value = start + count * interval) could be adopted back into the current generic implementation.

I notice that `start + count * interval` would actually work with `Int`, too.

I wonder if we might be better off introducing a sub-protocol of `Strideable` which can advance several increments at a time:

  protocol RandomStrideable: Strideable {
    func advancedBy(n: Self.Distance, times: Int) -> Self
  }
  
  extension RandomStrideable {
    func stride(to end: Self, by: Distance) -> RandomStrideTo<Self> { ... }
    func stride(through end: Self, by: Distance) -> RandomStrideThrough { ... }
  }

`Double` and `Int` can both conform to `RandomStrideable`; types which cannot do this (*are* there any types which cannot do this?) can conform to `Strideable`.

Alternatively, if we actually *prefer* the repeated-advance implementation and we're only using this multiplication-based one because we have to, `Strideable` can be the new multiplication-based protocol and `IncrementalStrideable` can be a subprotocol which supports repeated-advance.

It would still be called stride. I just call it fstride here because I needed to test and run it in current Swift.

-- E

···

On Mar 1, 2016, at 12:37 AM, Charles Kissinger <crk@akkyra.com> wrote:

+1 for the improved floating point algorithm. I think I would be in favor of still calling it “stride” rather than “fstride”, but I could be persuaded otherwise.

There seems to be some missing text in the proposal where I’ve commented below.

—CK

On Feb 29, 2016, at 5:16 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 29, 2016, at 5:03 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:
I agree, splitting into two proposals is a good idea.

-Joe

Decoupling Floating Point Strides from Generic Implementations

Proposal: SE-00XX
Author(s): Erica Sadun <http://github.com/erica&gt;
Status: TBD
Review manager: TBD
Swift strides create progressions along "notionally continuous one-dimensional values" using a series of offset values. This proposal replaces the Swift's generic stride implementation with seperate algorithms for integer strides (the current implementation) and floating point strides.

This proposal was discussed on-list in the "[Discussion] stride behavior and a little bit of a call-back to digital numbers" <http://article.gmane.org/gmane.comp.lang.swift.evolution/8014&gt;thread\.

<fpstride.md · GitHub

Strideable is genericized across both integer and floating point types. A single implementation causes floating point strides to accumulate errors through repeatedly adding by intervals. Floating point types deserve their own floating point-aware implementation that minimizes errors.

<fpstride.md · GitHub Art

A Strideable to sequence returns the sequence of values (self, self + stride, self + stride + stride, ... last) where last is the last value in the progression that is less than end.

A Strideable through sequence currently returns the sequence of values (self, self + stride, self + tride + stride, ... last) where last is the last value in the progression less than or equal to end. There is no guarantee that end is an element of the sequence.

While floating point calls present an extremely common use-case, they use integer-style math that accumulates errors during execution. Consider this example:

let ideal = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
print(zip(Array(1.0.stride(through: 2.01, by: 0.1)), ideal).map(-))
// prints [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16,
// 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16,
// 6.6613381477509392e-16, 6.6613381477509392e-16, 8.8817841970012523e-16,
// 8.8817841970012523e-16]
To create an array containing values from 1.0 to 2.0, the developer must add an epsilon value to the throughargument. Otherwise the stride progression ends near 1.9. Increasing the argument from 2.0 to 2.01 is sufficient to include the end value.
The errors in the sequence increase over time. You see this as errors become larger towards the end of the progress. This is an artifact of the generic implementation.
<fpstride.md · GitHub Design

Under the current implementation, each floating point addition in a generic stride accrues errors. The following progression never reaches 2.0.

print(Array(1.0.stride(through: 2.0, by: 0.1)))
// Prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
This same issue occurs with traditional C-style for loops. This is an artifact of floating point math, and not the specific Swift statements:

var array: [Double] =
for var i = 1.0; i <= 2.0; i += 0.1 {
    array.append(i)
}
print("Array", array)
// Prints Array [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
You should not have to manually add an epsilon to force a progression to complete.

Floating point strides are inherently dissimilar to and should not be genericized with integer strides. I propose separate their implementation, freeing them to provide their own specialized progressions, using better numeric methods. In doing so, floating point values are no longer tied to implementations that unnecessarily accrue errors or otherwise provide less-than-ideal solutions.

The following example provides a rough pass at what this might look like for floating point math. I leave specific algorithm details to experts; a decimal number solution would be more appropriate. The fun

*** What fun? ;-)

See: RandomAscii's write-ups on all things floating point <https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition&gt;\.

import Darwin

/// A `GeneratorType` for `DoubleStrideThrough`.
public struct DoubleStrideThroughGenerator : GeneratorType {
    let start: Double
    let end: Double
    let stride: Double
    var iteration: Int = 0
    var done: Bool = false

    public init(start: Double, end: Double, stride: Double) {
        (self.start, self.end, self.stride) = (start, end, stride)
    }

    /// Advance to the next element and return it, or `nil` if no next
    /// element exists.
    public mutating func next() -> Double? {
        if done {
            return nil
        }
        let current = start + Double(iteration) * stride; iteration += 1
        if signbit(current - end) == signbit(stride) { // thanks Joe Groff
            if abs(current) > abs(end) {
                done = true
                return current
            }
            return nil
        }
        return current
    }
}

public struct DoubleStrideThrough : SequenceType {
    let start: Double
    let end: Double
    let stride: Double

    /// Return a *generator* over the elements of this *sequence*.
    ///
    /// - Complexity: O(1).
    public func generate() -> DoubleStrideThroughGenerator {
        return DoubleStrideThroughGenerator(
            start: start, end: end, stride: stride)
    }

    init(start: Double, end: Double, stride: Double) {
        _precondition(stride != 0, "stride size must not be zero")
        (self.start, self.end, self.stride) = (start, end, stride)
    }

}

public extension Double {
    public func fstride(
        through end: Double, by stride: Double
        ) -> DoubleStrideThrough {
        return DoubleStrideThrough(
            start: self, end: end, stride: stride)
    }
}
This implementation reduces floating point error by limiting accumulated additions. It uses the current Swift 2.2 through semantics (versus the revised through semantics proposed under separate cover), so it never reaches 2.0 without adding an epsilon value.

print(Array(1.0.fstride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1000000000000001, 1.2, 1.3, 1.3999999999999999,
// 1.5, 1.6000000000000001, 1.7000000000000002, 1.8,
// 1.8999999999999999]

// versus the old style
print(Array(1.0.stride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1000000000000001, 1.2000000000000002, 1.3000000000000003,
// 1.4000000000000004, 1.5000000000000004, 1.6000000000000005,
// 1.7000000000000006, 1.8000000000000007, 1.9000000000000008]

print(zip(Array(1.0.stride(through: 2.0, by: 0.1)),
          Array(1.0.fstride(through: 2.0, by: 0.1))).map(-))
// prints [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16,
// 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16,
// 4.4408920985006262e-16, 6.6613381477509392e-16, 8.8817841970012523e-16]

let ideal = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
print(zip(Array(1.0.fstride(through: 2.0, by: 0.1)), ideal).map(-))
print(zip(Array(1.0.stride(through: 2.0, by: 0.1)), ideal).map(-))

// prints
// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.2204460492503131e-16, 0.0, 0.0]
// [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16,
// 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16,
// 6.6613381477509392e-16, 6.6613381477509392e-16, 8.8817841970012523e-16]
If one were looking for a quick and dirty fix, the same kind of math used in this rough solution (let value = start + count * interval) could be adopted back into the current generic implementation.

<fpstride.md · GitHub Considered

While precision math for decimal numbers would be better addressed by introducing a decimal type and/or warnings for at-risk floating point numbers, those features lie outside the scope of this proposal.

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

It does not produce the through value because that is how strides currently work. There's a second proposal to handle the through value.

-- E

···

On Mar 1, 2016, at 12:44 AM, Howard Lovatt <howard.lovatt@gmail.com> wrote:

Your implementation of `fstride` is faulty, it doesn't produce the `through` value. Suggest you change to:

public extension Double {
    public func doubleStride(through end: Double, by stride: Double) -> LazyMapCollection<Range<Int>, Double> {
        let limit = Int(trunc((end - self) / stride))
        return (0 ... limit).lazy.map { self + Double($0) * stride }
    }
}

  -- Howard.

On 1 March 2016 at 12:16, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 29, 2016, at 5:03 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:
I agree, splitting into two proposals is a good idea.

-Joe

Decoupling Floating Point Strides from Generic Implementations

Proposal: SE-00XX
Author(s): Erica Sadun <http://github.com/erica&gt;
Status: TBD
Review manager: TBD
Swift strides create progressions along "notionally continuous one-dimensional values" using a series of offset values. This proposal replaces the Swift's generic stride implementation with seperate algorithms for integer strides (the current implementation) and floating point strides.

This proposal was discussed on-list in the "[Discussion] stride behavior and a little bit of a call-back to digital numbers" <http://article.gmane.org/gmane.comp.lang.swift.evolution/8014&gt;thread\.

<fpstride.md · GitHub

Strideable is genericized across both integer and floating point types. A single implementation causes floating point strides to accumulate errors through repeatedly adding by intervals. Floating point types deserve their own floating point-aware implementation that minimizes errors.

<fpstride.md · GitHub Art

A Strideable to sequence returns the sequence of values (self, self + stride, self + stride + stride, ... last) where last is the last value in the progression that is less than end.

A Strideable through sequence currently returns the sequence of values (self, self + stride, self + tride + stride, ... last) where last is the last value in the progression less than or equal to end. There is no guarantee that end is an element of the sequence.

While floating point calls present an extremely common use-case, they use integer-style math that accumulates errors during execution. Consider this example:

let ideal = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]
print(zip(Array(1.0.stride(through: 2.01, by: 0.1)), ideal).map(-))
// prints [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16,
// 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16,
// 6.6613381477509392e-16, 6.6613381477509392e-16, 8.8817841970012523e-16,
// 8.8817841970012523e-16]
To create an array containing values from 1.0 to 2.0, the developer must add an epsilon value to the throughargument. Otherwise the stride progression ends near 1.9. Increasing the argument from 2.0 to 2.01 is sufficient to include the end value.
The errors in the sequence increase over time. You see this as errors become larger towards the end of the progress. This is an artifact of the generic implementation.
<fpstride.md · GitHub Design

Under the current implementation, each floating point addition in a generic stride accrues errors. The following progression never reaches 2.0.

print(Array(1.0.stride(through: 2.0, by: 0.1)))
// Prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
This same issue occurs with traditional C-style for loops. This is an artifact of floating point math, and not the specific Swift statements:

var array: [Double] =
for var i = 1.0; i <= 2.0; i += 0.1 {
    array.append(i)
}
print("Array", array)
// Prints Array [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
You should not have to manually add an epsilon to force a progression to complete.

Floating point strides are inherently dissimilar to and should not be genericized with integer strides. I propose separate their implementation, freeing them to provide their own specialized progressions, using better numeric methods. In doing so, floating point values are no longer tied to implementations that unnecessarily accrue errors or otherwise provide less-than-ideal solutions.

The following example provides a rough pass at what this might look like for floating point math. I leave specific algorithm details to experts; a decimal number solution would be more appropriate. The fun

See: RandomAscii's write-ups on all things floating point <https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition&gt;\.

import Darwin

/// A `GeneratorType` for `DoubleStrideThrough`.
public struct DoubleStrideThroughGenerator : GeneratorType {
    let start: Double
    let end: Double
    let stride: Double
    var iteration: Int = 0
    var done: Bool = false

    public init(start: Double, end: Double, stride: Double) {
        (self.start, self.end, self.stride) = (start, end, stride)
    }

    /// Advance to the next element and return it, or `nil` if no next
    /// element exists.
    public mutating func next() -> Double? {
        if done {
            return nil
        }
        let current = start + Double(iteration) * stride; iteration += 1
        if signbit(current - end) == signbit(stride) { // thanks Joe Groff
            if abs(current) > abs(end) {
                done = true
                return current
            }
            return nil
        }
        return current
    }
}

public struct DoubleStrideThrough : SequenceType {
    let start: Double
    let end: Double
    let stride: Double

    /// Return a *generator* over the elements of this *sequence*.
    ///
    /// - Complexity: O(1).
    public func generate() -> DoubleStrideThroughGenerator {
        return DoubleStrideThroughGenerator(
            start: start, end: end, stride: stride)
    }

    init(start: Double, end: Double, stride: Double) {
        _precondition(stride != 0, "stride size must not be zero")
        (self.start, self.end, self.stride) = (start, end, stride)
    }

}

public extension Double {
    public func fstride(
        through end: Double, by stride: Double
        ) -> DoubleStrideThrough {
        return DoubleStrideThrough(
            start: self, end: end, stride: stride)
    }
}
This implementation reduces floating point error by limiting accumulated additions. It uses the current Swift 2.2 through semantics (versus the revised through semantics proposed under separate cover), so it never reaches 2.0 without adding an epsilon value.

print(Array(1.0.fstride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1000000000000001, 1.2, 1.3, 1.3999999999999999,
// 1.5, 1.6000000000000001, 1.7000000000000002, 1.8,
// 1.8999999999999999]

// versus the old style
print(Array(1.0.stride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1000000000000001, 1.2000000000000002, 1.3000000000000003,
// 1.4000000000000004, 1.5000000000000004, 1.6000000000000005,
// 1.7000000000000006, 1.8000000000000007, 1.9000000000000008]

print(zip(Array(1.0.stride(through: 2.0, by: 0.1)),
          Array(1.0.fstride(through: 2.0, by: 0.1))).map(-))
// prints [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16,
// 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16,
// 4.4408920985006262e-16, 6.6613381477509392e-16, 8.8817841970012523e-16]

let ideal = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
print(zip(Array(1.0.fstride(through: 2.0, by: 0.1)), ideal).map(-))
print(zip(Array(1.0.stride(through: 2.0, by: 0.1)), ideal).map(-))

// prints
// [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.2204460492503131e-16, 0.0, 0.0]
// [0.0, 0.0, 2.2204460492503131e-16, 2.2204460492503131e-16,
// 4.4408920985006262e-16, 4.4408920985006262e-16, 4.4408920985006262e-16,
// 6.6613381477509392e-16, 6.6613381477509392e-16, 8.8817841970012523e-16]
If one were looking for a quick and dirty fix, the same kind of math used in this rough solution (let value = start + count * interval) could be adopted back into the current generic implementation.

<fpstride.md · GitHub Considered

While precision math for decimal numbers would be better addressed by introducing a decimal type and/or warnings for at-risk floating point numbers, those features lie outside the scope of this proposal.

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

I take issue with the proposed definition of "through" used. When Googling
"definition of through", the example sentence used for the definition Erica
provided is "The approach to the church is through the gate". Doesn't sound
like we're discussing ranges there; sounds like location.

A more range-based definition that pops up is "up to and including (a
particular point in an ordered sequence)", which sounds exactly like the
way "through" is being used today in stride. Example sentence given: "They
will be in town from March 24 through May 7". Commonly, English speakers
often refer to the numbers "1 through 10", which refers to [1, 10].

I also have a strong distaste for "towards", as the ambiguity that Xiaodi
mentioned makes it seem like you could reach any point before 10 in the
range [1, 10), be it 2 all the way through 9 (notice I use "through" here
to mean [2, 9]).

I feel like ultimately this proposal makes the semantics of stride less
clear. While the naming today seems pretty obvious to me, I would
absolutely have to frequently refer to documentation if the proposed
changes were accepted. The changes proposed don't seem to match the
semantics of words used every day to describe ranges.

Thanks,
Seth

···

On Tue, Mar 1, 2016 at 1:00 AM Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

I still wonder if a better solution might involve the same syntax as
ranges currently benefit from, i.e:

0 ..< 10 // [0, 10) with an increment of 1
(0 … 10).stride(2) // [0, 10] with an increment of 2

The most important change is that the default type for this should be able
to handle higher starting indices, e.g:

(10 … 0).stride(2) // [10, 0] with a decrement of 2

Basically I don’t like the stride global function in the first place =)

The benefit of the Range syntax is that it’s clear whether the end point
is inclusive or exclusive, and it’s nice and succinct. The problem right
now is just that ranges have a limit on the direction they can be traversed
in for things like accessing slices of collections, in which case we’ll
need to make sure that these still retain the same limitation.

On 1 Mar 2016, at 08:54, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:

It's so nice to see such care devoted to clarifying these existing
names. I agree with the premise that stride(to:by:) and
stride(through:by:) are poorly named, but I'd like to make two
critiques of this proposal--

Critique 1:

The basic distinction between the two current stride styles is that
one is inclusive and the other is exclusive of the end value. I agree
with you that "to" doesn't imply an exclusive end value, but "towards"
doesn't imply that the parameter is any sort of end value at
all--rather, it implies just a direction (or as you quote from the
NOAD, getting close or closer).

Two implications:

First, if I stride from 10 towards 0 by 1, by the plain English
meaning of the word "towards", I would expect to obtain 10, 9, 8, 7,
6, etc. If we simply rename stride(to:by:) to stride(towards:by:), I
would not get that result. By contrast, it makes sense from the
current name that stride(to:by:) attempts to increment using the `by`
parameter without considering whether the end value is greater than or
less than the start value; if you can't get from here "to" there by
such increments, too bad!

Second, if I stride from 0 towards 10 by 1 (in the English language,
not in Swift), I may or may not stop short of 10 itself. That is,
whether "towards" is inclusive or exclusive of the end value can't be
inferred from the meaning of the word; after all, if I'm making
strides towards a goal, I do intend to reach it, or at least that's
what I tell people when they ask how my PhD is going...

Generalizing from the word "towards", I don't know that any two
prepositions in the English language can be used unambiguously to
convey the distinction between inclusive and exclusive end values.
Although, on some additional thought--if I had to suggest a
preposition, perhaps "until" or "till" would be more apt than
"towards".

The saving grace of "to" and "through" in the current situation is
that the latter seems intuitively to go further than the former, and
if one deduces by analogy with the range operators that one of these
must exclude the end value and the other include it, then the two
names must mean what they do today. With three stride styles and three
prepositions, but only two range operators, this intuition is broken,
while the prepositions may not get much clearer (though I must admit
that your proposed use of "to" is an improvement).

Critique 2:

The original motivation behind your twin proposals was the epsilon
adjustment necessary for floating point end values. Your other
proposal fixes an issue with accumulated errors but doesn't solve the
need for an epsilon adjustment. Here, you propose adding a third
stride style to solve that problem, along the way shuffling the naming
of the existing stride styles. Since you haven't presented other use
cases for that third stride style here, and you haven't listed
alternatives considered for solving the original motivating problem
(i.e. epsilon adjustment), let me propose one alternative:

Keep the naming of stride styles as-is (inapt as they may be), and for
floating point end values make stride(through: aNumber, by: something)
equivalent to stride(to: theNextLargestRepresentableNumber, by:
somethingPositive) or stride(to: theNextSmallestRepresentableNumber,
by: somethingNegative). Would that solve your original issue
adequately?

Alternatively, if there are lots of examples that can be envisioned
for this third stride style, would the same examples suggest perhaps
that `..>` might be a useful third range operator?

On Mon, Feb 29, 2016 at 7:14 PM, Erica Sadun via swift-evolution > <swift-evolution@swift.org> wrote:

On Feb 29, 2016, at 5:03 PM, Joe Groff <jgroff@apple.com> wrote:
I agree, splitting into two proposals is a good idea.

-Joe

Conventionalizing stride semantics

Proposal: SE-00NN
Author(s): Erica Sadun
Status: TBD
Review manager: TBD

Swift offers two stride functions, stride(to:, by:) and stride(through:,
by:). This proposal introduces a third style and renames the existing to
and
through styles.

This proposal was discussed on-list in the "[Discussion] stride behavior
and
a little bit of a call-back to digital numbers"thread.

Motivation

Strideable's function names do not semantically match the progressions they
generate. Values produced by throughdo not pass through an end point; they
stop at or before that fence. For example, 1.stride(through: 10, by: 8)
returns the progress (1, 9), not (1, 9, 17). Similarly, its to function
values reaches its end point. 1.stride(to:4, by:1) returns 1, 2, and 3. It
never makes it to 4:

The current Swift definition of to returns values in [start, end) and will
never reach end. In other words, you will never get to end.
The current Swift definition of through returns values in [start, end]. It
may never reach end and certainly never goes through that value.

Some definitions with the help of the New Oxford American Dictionary

Moving to a value expresses "a point reached at the end of a range".
To pass through a value, you should move beyond "the position or location
of
something beyond or at the far end of (an opening or an obstacle)".
To move towards a value is to get "close or closer" or "getting closer to
achieving (a goal)".

Current Art

A Strideable to sequence returns the sequence of values (self, self +
stride, self + stride + stride, ... last) where last is the last value in
the progression that is less than end.

A Strideable through sequence currently returns the sequence of values
(self, self + stride, self + tride + stride, ... last) where last is the
last value in the progression less than or equal to end. There is no
guarantee that end is an element of the sequence.

The name of the calling function through suggests the progression will pass
through the end point before stopping. It does not. The name to suggests a
progression will attempt to arrive at an end point. It does not.

Detail Design

When striding to or through a number, the behavior does not match the
meaning of the word. Swift should provide three stride styles not two.

Style 1: [start, end) by interval
This style is currently called to. I propose to rename it towards as each
value works towards end. The final value in the progression is less than
end

Style 2: [start, end] by interval
This style is currently called through. I propose to rename it to. The
progression concludes with a value that is less than or equal to end. Swift
provides no guarantee that end is an element of the sequence.

Style 3: [start, >=end] by interval
I propose to introduce a new style called through. The final value is
guaranteed to pass through end, either by finishing on end or past end. The
final value is strictly less than end + interval.

A Style 3 implementation works as follows:

/// A `Strideable through` sequence currently returns the sequence of
values
/// (`self`, `self + stride`, `self + stride + stride`, ... *last*) where
*last*
/// is the first value in the progression **greater than or equal to**
`end`.
/// There is no guarantee that `end` is an element of the sequence.

   /// Advance to the next element and return it, or `nil` if no next
   /// element exists.
   public mutating func next() -> Element? {
       if done {
           return nil
       }
       if stride > 0 ? current >= end : current <= end {
           done = true
           return current
       }
       let result = current
       current = current.advancedBy(stride)
       return result
   }
}

This solution is minimally disruptive to developers, respectful to existing
code bases, and introduces a more complete semantic set of progressions
that
better matches progression names to developer expectations. (For example,
"this argument says it goes through a value but it never even reaches that
value".)

Upon adopting this change, out-of-sync strides now pass through end values:

// Unit stride
print(Array(1.stride(through: 10, by: 1)))
// prints [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], no change

// Old out-of-sync stride
print(Array(1.stride(through: 10, by: 8)))
// prints [1, 9]

// New out-of-sync stride
print(Array(1.stride(through: 10, by: 8)))
// prints[1, 9, 17]

There are no functional changes existing stride implementations. Only their
names change.

print(Array(1.stride(towards: 10, by: 8))) // was `to`
// prints [1, 9]

print(Array(1.stride(to: 10, by: 8))) // was `through`
// prints [1, 9]

Although floating point arithmetic presents a separate and orthogonal
challenge, its behavior changes if this proposal is implemented under the
current generic system. For example, through now includes a value at (or at
least close to) 2.0 instead of stopping at 1.9 due to accumulated floating
point errors.

// Old
print(Array(1.0.stride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]

// New
print(Array(1.0.stride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]

// Old, does not pass through 1.9
print(Array(1.0.stride(through: 1.9, by: 0.25)))
// prints [1.0, 1.25, 1.5, 1.75]

// New, passes through 1.9
print(Array(1.0.stride(through: 1.9, by: 0.25)))
// prints [1.0, 1.25, 1.5, 1.75, 2.0]

Impact on Existing Code

Renaming two stride functions and adding a third does not change or break
existing code. The Swift 3 migrator can easily update the names for the two
existing styles. That said, the migrator will not find in-place workarounds
like a through: 2.01 epsilon adjustment to correct for floating-point
fences. By adding FIXME: notes wherever through: is found and renamed to
to:, the migrator could warn against continued use without a full
inspection
and could offer links to information about the semantic changes.

Alternatives Considered

The only alternative at this time is "no change" to existing semantics.

_______________________________________________
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

If you adopt a better algorithm, like the one in the updated proposal (https://gist.github.com/erica/cf50f3dc54bb3a090933\),
would it still make sense to do this? The one time set-up for the generator is "expensive" if run at each access this way.

-- Erica

···

On Mar 1, 2016, at 3:56 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

If one were looking for a quick and dirty fix, the same kind of math used in this rough solution (let value = start + count * interval) could be adopted back into the current generic implementation.

I notice that `start + count * interval` would actually work with `Int`, too.

I wonder if we might be better off introducing a sub-protocol of `Strideable` which can advance several increments at a time:

  protocol RandomStrideable: Strideable {
    func advancedBy(n: Self.Distance, times: Int) -> Self
  }
  
  extension RandomStrideable {
    func stride(to end: Self, by: Distance) -> RandomStrideTo<Self> { ... }
    func stride(through end: Self, by: Distance) -> RandomStrideThrough { ... }
  }

`Double` and `Int` can both conform to `RandomStrideable`; types which cannot do this (*are* there any types which cannot do this?) can conform to `Strideable`.

Alternatively, if we actually *prefer* the repeated-advance implementation and we're only using this multiplication-based one because we have to, `Strideable` can be the new multiplication-based protocol and `IncrementalStrideable` can be a subprotocol which supports repeated-advance.

--
Brent Royal-Gordon
Architechies

I can't remember if I responded to this yet or not, since I just added an algorithm to the proposal and am finishing tweaks.

Strides can progress negatively as well as positively, and ranges don't support that.

(0.0)...(-1.2) returns fatal error: Invalid ClosedInterval bounds (end < start)

Take a look at the updated proposal with the more precise second algorithm.

-- E

···

On Mar 1, 2016, at 12:44 AM, Howard Lovatt <howard.lovatt@gmail.com> wrote:

Your implementation of `fstride` is faulty, it doesn't produce the `through` value. Suggest you change to:

public extension Double {
    public func doubleStride(through end: Double, by stride: Double) -> LazyMapCollection<Range<Int>, Double> {
        let limit = Int(trunc((end - self) / stride))
        return (0 ... limit).lazy.map { self + Double($0) * stride }
    }
}

  -- Howard.

On 1 March 2016 at 12:16, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Feb 29, 2016, at 5:03 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:
I agree, splitting into two proposals is a good idea.

-Joe

This would have to be a separate proposal to allow descending ranges. I think that would be a good addition to the language and encourage you to write it.

-- E

···

On Mar 1, 2016, at 2:00 AM, Haravikk <swift-evolution@haravikk.me> wrote:

I still wonder if a better solution might involve the same syntax as ranges currently benefit from, i.e:

  0 ..< 10 // [0, 10) with an increment of 1
  (0 … 10).stride(2) // [0, 10] with an increment of 2

The most important change is that the default type for this should be able to handle higher starting indices, e.g:

  (10 … 0).stride(2) // [10, 0] with a decrement of 2

Basically I don’t like the stride global function in the first place =)

The benefit of the Range syntax is that it’s clear whether the end point is inclusive or exclusive, and it’s nice and succinct. The problem right now is just that ranges have a limit on the direction they can be traversed in for things like accessing slices of collections, in which case we’ll need to make sure that these still retain the same limitation.

Updated proposal: stride.md · GitHub

···

On Mar 1, 2016, at 10:36 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 1, 2016, at 1:54 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

It's so nice to see such care devoted to clarifying these existing
names. I agree with the premise that stride(to:by:) and
stride(through:by:) are poorly named, but I'd like to make two
critiques of this proposal--

Inline below.

Do you want the stride to pick up the sign automatically? Instead of

print(Array(10.stride(to: 0, by: -1))) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
current Swift

have

print(Array(10.stride(to: 0, by: 1))) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
adjusted sign

Or am I missing your point of "if I stride from 10 towards 0 by 1"?

To clarify, I am not troubled that stride(to:by:) as it is now doesn't
pick up the sign. The point is that, if renamed to
stride(towards:by:), the English meaning of "towards" implies that it
would pick up the sign. It is a critique of the suggested renaming,
not a critique of the algorithm being renamed.

I'm searching for a word that means "in the direction of but never
reaching".

print(Array(10.stride(to: 0, by: -1))) // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
current Swift

This never reaches 0.

I understand the aim, and I'd be greatly pleased if you found such a
word. Here my point is that "towards" doesn't quite fulfill that
purpose because, in English, (a) making strides "towards" something
has no relation to whether one in fact gets there; and (b) the
intention behind making strides "towards" something is that one does
in fact get there. The only thing that "towards" unambiguously
suggests (IMO) is that it picks up the sign, which is the one thing
that stride(towards:by:) would not do after a simple renaming from
stride(to:by:).

`until` to me suggests reaching the end-point.

If I tell someone that I'm here "until" 6 p.m., I would hope that they
do not try to find me there at 6. The implication is that, at 6, I get
up to leave.

But that's tangential to the point I'm trying to make, which is this--

Proposition 1: "Until" is one of the only words I can find where the
dictionary gives at least one definition that suggests never reaching
the end point. [For example: OED until 6(B)(c): "In similar sense
without a negative: Before the time that; before. Cf. till conj. 1c."]

Proposition 2: [I think we both accept this.] There are other meanings
of the word "until" that do suggest reaching the end point.

Conclusion with a little leap of logic: I can't think of a word in
English without this ambiguity. Clarity in naming these stride styles
may have to rely on factors other than the English meaning of the the
prepositions used for labels. One such source of clarity (for me) are
the parallels between the two stride styles and two range operators.

Quite often, it doesn't really:

print(Array(1.stride(to:10, by: 8))) // [1, 9]
print(Array(1.stride(through:10, by: 8))) // [1, 9]

I'm aware, and it doesn't bother me. I'm satisfied that *when* the two
do differ, `through` goes further than `to`, as their names would
imply. The argument is that this is enough to deduce, by analogy with
range operators, that one is closed and the other half-open.

Actually, I've tweaked it this morning to take care of that. Reload:
fpstride.md · GitHub

I must misunderstand what it is you tweaked. You still write that the
other proposal doesn't remove the need for manual epsilon adjustment?

It's really solving the 1/10/by 8 problem more than the 2.0 problem. Which
is a big reason why I even
brought up the issue of separating the proposals in the first place. To me,
the following just doesn't make
sense:

print(Array(1.stride(through:10, by: 8))) // [1, 9]

I'm not pleased with it, but I can make sense of it if I contort my
thinking a little:
Take the numbers 1 through 10, stride through that range by 8. You
must get: 1, 9.

In other words, the contortion I must accept is this: "through"
doesn't go with "stride", even though it should. Mentally rearrange to
put "through" between start and end, moving the word "stride" after
the end: 1 through 10, stride by 8. With this approach, I can
understand why someone would name the two stride styles "to" and
"through". Consider the typical (American?) English phrase "open Mon
thru Fri, 9 to 5": Fri is included, but 5 is excluded.

Canonical use-cases for all three styles:

1 towards 5 by 1: [1, 2, 3, 4]
This style mimics a..<b but allows non-unit and negative progressions

1 to 5 by 1: [1, 2, 3, 4, 5]
This style mimics a...b but allows non-unit and negative progressions

1 through 10 by 8: [1, 9, 17]
This style ensures a non-unit and negative progression that passes to or
through the final value,
ensuring that the range of the progression fully includes the range of the
from and to values:
[first...last] subsumes [from...through]. You might call this a..>b

I understand that you believe this makes the behavior of "through"
more sensible. I could even agree. But do we need this third stride
style, whatever it's called? Essentially, my question is: besides the
issue of epsilon adjustments, when have you encountered a case in your
code where you've needed to stride beyond the end point?

I like that quite a lot actually.

:) Thanks.

···

On Tue, Mar 1, 2016 at 11:36 AM, Erica Sadun <erica@ericasadun.com> wrote:

A suggestion: "stride(approaching:by:)"

···

On Tue, Mar 1, 2016 at 5:55 PM, Erica Sadun via swift-evolution < swift-evolution@swift.org> wrote:

Please disregard previous reply. Just realized, what about 10..>1 by -1?
upTo: doesn't work there.

> On Mar 1, 2016, at 10:52 AM, Dmitri Gribenko <gribozavr@gmail.com> > wrote:
>
> On Mon, Feb 29, 2016 at 5:14 PM, Erica Sadun via swift-evolution > > <swift-evolution@swift.org> wrote:
>> Style 1: [start, end) by interval
>> This style is currently called to. I propose to rename it towards as
each
>> value works towards end. The final value in the progression is less
than end
>
> The current usage of 'through' seems unambiguous to me. 'to' could be
> improved by using 'upTo' instead.
>
> stride(from: 1, upTo: 10, by: 1) // [1, 10)
>
> 'upTo' is already precedented in Collection.prefix(upTo:).
>
> Dmitri
>
> --
> main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
> (j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

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

Please disregard previous reply. Just realized, what about 10..>1 by -1? upTo: doesn't work there.

“Toward” doesn't work either unless the stride amount matches.

···

on Tue Mar 01 2016, Erica Sadun <swift-evolution@swift.org> wrote:

On Mar 1, 2016, at 10:52 AM, Dmitri Gribenko <gribozavr@gmail.com> wrote:

On Mon, Feb 29, 2016 at 5:14 PM, Erica Sadun via swift-evolution >> <swift-evolution@swift.org> wrote:

Style 1: [start, end) by interval
This style is currently called to. I propose to rename it towards as each
value works towards end. The final value in the progression is less than end

The current usage of 'through' seems unambiguous to me. 'to' could be
improved by using 'upTo' instead.

stride(from: 1, upTo: 10, by: 1) // [1, 10)

'upTo' is already precedented in Collection.prefix(upTo:).

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/

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

--
-Dave

I still wonder if a better solution might involve the same syntax as ranges currently benefit from, i.e:

  0 ..< 10 // [0, 10) with an increment of 1
  (0 … 10).stride(2) // [0, 10] with an increment of 2

The most important change is that the default type for this should be able to handle higher starting indices, e.g:

  (10 … 0).stride(2) // [10, 0] with a decrement of 2

I like this approach a lot. We have lots of different ways to express
variations on ranges and intervals today that don't actually involve the
range operators (including prefixTo, suffixFrom, stride). IMO they
should.

The one problem I anticipate is that negative strides won't work well,
because forming (a...b) will have a precondition that a <= b.

···

on Tue Mar 01 2016, Haravikk <swift-evolution@swift.org> wrote:

Basically I don’t like the stride global function in the first place =)

The benefit of the Range syntax is that it’s clear whether the end
point is inclusive or exclusive, and it’s nice and succinct. The
problem right now is just that ranges have a limit on the direction
they can be traversed in for things like accessing slices of
collections, in which case we’ll need to make sure that these still
retain the same limitation.

On 1 Mar 2016, at 08:54, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

It's so nice to see such care devoted to clarifying these existing
names. I agree with the premise that stride(to:by:) and
stride(through:by:) are poorly named, but I'd like to make two
critiques of this proposal--

Critique 1:

The basic distinction between the two current stride styles is that
one is inclusive and the other is exclusive of the end value. I agree
with you that "to" doesn't imply an exclusive end value, but "towards"
doesn't imply that the parameter is any sort of end value at
all--rather, it implies just a direction (or as you quote from the
NOAD, getting close or closer).

Two implications:

First, if I stride from 10 towards 0 by 1, by the plain English
meaning of the word "towards", I would expect to obtain 10, 9, 8, 7,
6, etc. If we simply rename stride(to:by:) to stride(towards:by:), I
would not get that result. By contrast, it makes sense from the
current name that stride(to:by:) attempts to increment using the `by`
parameter without considering whether the end value is greater than or
less than the start value; if you can't get from here "to" there by
such increments, too bad!

Second, if I stride from 0 towards 10 by 1 (in the English language,
not in Swift), I may or may not stop short of 10 itself. That is,
whether "towards" is inclusive or exclusive of the end value can't be
inferred from the meaning of the word; after all, if I'm making
strides towards a goal, I do intend to reach it, or at least that's
what I tell people when they ask how my PhD is going...

Generalizing from the word "towards", I don't know that any two
prepositions in the English language can be used unambiguously to
convey the distinction between inclusive and exclusive end values.
Although, on some additional thought--if I had to suggest a
preposition, perhaps "until" or "till" would be more apt than
"towards".

The saving grace of "to" and "through" in the current situation is
that the latter seems intuitively to go further than the former, and
if one deduces by analogy with the range operators that one of these
must exclude the end value and the other include it, then the two
names must mean what they do today. With three stride styles and three
prepositions, but only two range operators, this intuition is broken,
while the prepositions may not get much clearer (though I must admit
that your proposed use of "to" is an improvement).

Critique 2:

The original motivation behind your twin proposals was the epsilon
adjustment necessary for floating point end values. Your other
proposal fixes an issue with accumulated errors but doesn't solve the
need for an epsilon adjustment. Here, you propose adding a third
stride style to solve that problem, along the way shuffling the naming
of the existing stride styles. Since you haven't presented other use
cases for that third stride style here, and you haven't listed
alternatives considered for solving the original motivating problem
(i.e. epsilon adjustment), let me propose one alternative:

Keep the naming of stride styles as-is (inapt as they may be), and for
floating point end values make stride(through: aNumber, by: something)
equivalent to stride(to: theNextLargestRepresentableNumber, by:
somethingPositive) or stride(to: theNextSmallestRepresentableNumber,
by: somethingNegative). Would that solve your original issue
adequately?

Alternatively, if there are lots of examples that can be envisioned
for this third stride style, would the same examples suggest perhaps
that `..>` might be a useful third range operator?

On Mon, Feb 29, 2016 at 7:14 PM, Erica Sadun via swift-evolution >> <swift-evolution@swift.org> wrote:

On Feb 29, 2016, at 5:03 PM, Joe Groff <jgroff@apple.com> wrote:
I agree, splitting into two proposals is a good idea.

-Joe

Conventionalizing stride semantics

Proposal: SE-00NN
Author(s): Erica Sadun
Status: TBD
Review manager: TBD

Swift offers two stride functions, stride(to:, by:) and stride(through:,
by:). This proposal introduces a third style and renames the existing to and
through styles.

This proposal was discussed on-list in the "[Discussion] stride behavior and
a little bit of a call-back to digital numbers"thread.

Motivation

Strideable's function names do not semantically match the progressions they
generate. Values produced by throughdo not pass through an end point; they
stop at or before that fence. For example, 1.stride(through: 10, by: 8)
returns the progress (1, 9), not (1, 9, 17). Similarly, its to function
values reaches its end point. 1.stride(to:4, by:1) returns 1, 2, and 3. It
never makes it to 4:

The current Swift definition of to returns values in [start, end) and will
never reach end. In other words, you will never get to end.
The current Swift definition of through returns values in [start, end]. It
may never reach end and certainly never goes through that value.

Some definitions with the help of the New Oxford American Dictionary

Moving to a value expresses "a point reached at the end of a range".
To pass through a value, you should move beyond "the position or location of
something beyond or at the far end of (an opening or an obstacle)".
To move towards a value is to get "close or closer" or "getting closer to
achieving (a goal)".

Current Art

A Strideable to sequence returns the sequence of values (self, self +
stride, self + stride + stride, ... last) where last is the last value in
the progression that is less than end.

A Strideable through sequence currently returns the sequence of values
(self, self + stride, self + tride + stride, ... last) where last is the
last value in the progression less than or equal to end. There is no
guarantee that end is an element of the sequence.

The name of the calling function through suggests the progression will pass
through the end point before stopping. It does not. The name to suggests a
progression will attempt to arrive at an end point. It does not.

Detail Design

When striding to or through a number, the behavior does not match the
meaning of the word. Swift should provide three stride styles not two.

Style 1: [start, end) by interval
This style is currently called to. I propose to rename it towards as each
value works towards end. The final value in the progression is less than end

Style 2: [start, end] by interval
This style is currently called through. I propose to rename it to. The
progression concludes with a value that is less than or equal to end. Swift
provides no guarantee that end is an element of the sequence.

Style 3: [start, >=end] by interval
I propose to introduce a new style called through. The final value is
guaranteed to pass through end, either by finishing on end or past end. The
final value is strictly less than end + interval.

A Style 3 implementation works as follows:

/// A `Strideable through` sequence currently returns the sequence of values
/// (`self`, `self + stride`, `self + stride + stride`, ... *last*) where
*last*
/// is the first value in the progression **greater than or equal to**
`end`.
/// There is no guarantee that `end` is an element of the sequence.

   /// Advance to the next element and return it, or `nil` if no next
   /// element exists.
   public mutating func next() -> Element? {
       if done {
           return nil
       }
       if stride > 0 ? current >= end : current <= end {
           done = true
           return current
       }
       let result = current
       current = current.advancedBy(stride)
       return result
   }
}

This solution is minimally disruptive to developers, respectful to existing
code bases, and introduces a more complete semantic set of progressions that
better matches progression names to developer expectations. (For example,
"this argument says it goes through a value but it never even reaches that
value".)

Upon adopting this change, out-of-sync strides now pass through end values:

// Unit stride
print(Array(1.stride(through: 10, by: 1)))
// prints [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], no change

// Old out-of-sync stride
print(Array(1.stride(through: 10, by: 8)))
// prints [1, 9]

// New out-of-sync stride
print(Array(1.stride(through: 10, by: 8)))
// prints[1, 9, 17]

There are no functional changes existing stride implementations. Only their
names change.

print(Array(1.stride(towards: 10, by: 8))) // was `to`
// prints [1, 9]

print(Array(1.stride(to: 10, by: 8))) // was `through`
// prints [1, 9]

Although floating point arithmetic presents a separate and orthogonal
challenge, its behavior changes if this proposal is implemented under the
current generic system. For example, through now includes a value at (or at
least close to) 2.0 instead of stopping at 1.9 due to accumulated floating
point errors.

// Old
print(Array(1.0.stride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]

// New
print(Array(1.0.stride(through: 2.0, by: 0.1)))
// prints [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0]

// Old, does not pass through 1.9
print(Array(1.0.stride(through: 1.9, by: 0.25)))
// prints [1.0, 1.25, 1.5, 1.75]

// New, passes through 1.9
print(Array(1.0.stride(through: 1.9, by: 0.25)))
// prints [1.0, 1.25, 1.5, 1.75, 2.0]

Impact on Existing Code

Renaming two stride functions and adding a third does not change or break
existing code. The Swift 3 migrator can easily update the names for the two
existing styles. That said, the migrator will not find in-place workarounds
like a through: 2.01 epsilon adjustment to correct for floating-point
fences. By adding FIXME: notes wherever through: is found and renamed to
to:, the migrator could warn against continued use without a full inspection
and could offer links to information about the semantic changes.

Alternatives Considered

The only alternative at this time is "no change" to existing semantics.

_______________________________________________
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

--
-Dave

Float and CGFloat pop to mind.

···

On Mar 1, 2016, at 4:41 AM, Dany St-Amant via swift-evolution <swift-evolution@swift.org> wrote:
I was thinking about something similar, but as precise Strideable (direct increment ok) and impreciseStrideable (multiplicative must be used). But my main question is reversed, beside Double what other type/concept would require the multiplicative approached?

Dany

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

I'm open to other name suggestions for the three styles.

-- E

···

On Mar 1, 2016, at 2:18 AM, Seth Friedman via swift-evolution <swift-evolution@swift.org> wrote:

I take issue with the proposed definition of "through" used. When Googling "definition of through", the example sentence used for the definition Erica provided is "The approach to the church is through the gate". Doesn't sound like we're discussing ranges there; sounds like location.

A more range-based definition that pops up is "up to and including (a particular point in an ordered sequence)", which sounds exactly like the way "through" is being used today in stride. Example sentence given: "They will be in town from March 24 through May 7". Commonly, English speakers often refer to the numbers "1 through 10", which refers to [1, 10].

I also have a strong distaste for "towards", as the ambiguity that Xiaodi mentioned makes it seem like you could reach any point before 10 in the range [1, 10), be it 2 all the way through 9 (notice I use "through" here to mean [2, 9]).

I feel like ultimately this proposal makes the semantics of stride less clear. While the naming today seems pretty obvious to me, I would absolutely have to frequently refer to documentation if the proposed changes were accepted. The changes proposed don't seem to match the semantics of words used every day to describe ranges.

Thanks,
Seth

To clarify, I am not troubled that stride(to:by:) as it is now doesn't
pick up the sign. The point is that, if renamed to
stride(towards:by:), the English meaning of "towards" implies that it
would pick up the sign. It is a critique of the suggested renaming,
not a critique of the algorithm being renamed.

Summarizing: "Using `towards` suggests that the by value picks up the vector direction and
can be misleading.

Response:

1. yeah.
2. but no solution is going to be ideal.
3. alternatives: approaching, movingTowards, advancedTowards. Included in the latest
revision of the proposal.

My big issues are "this thing says it goes through and it doesn't, this thing says it goes to and it doesn't".
So long as those are fixed reasonably well, I am happy, even without the naming being perfect.

I must misunderstand what it is you tweaked. You still write that the
other proposal doesn't remove the need for manual epsilon adjustment?

In order to separate this into two proposals, I had to make sure they weren't depending on each other.
So this proposal *only* addresses the semantic mismatch I described 4 lines up.

Manual epsilon adjustment is simply a floating point thing, and is discussed at length in the
other proposal (Again, keep refreshing gist.github because both are works in progress.)

[first...last] subsumes [from...through]. You might call this a..>b

I understand that you believe this makes the behavior of "through"
more sensible. I could even agree. But do we need this third stride
style, whatever it's called? Essentially, my question is: besides the
issue of epsilon adjustments, when have you encountered a case in your
code where you've needed to stride beyond the end point?

First, I renamed it with the two options a...>b, a..>=b and a..=>b to suggest "reach or greater".

Second, yes, and I added a big new section on canonical use cases.

-- E

···

On Mar 1, 2016, at 12:25 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

In other words, the contortion I must accept is this: "through"
doesn't go with "stride", even though it should. Mentally rearrange to
put "through" between start and end, moving the word "stride" after
the end: 1 through 10, stride by 8. With this approach, I can
understand why someone would name the two stride styles "to" and
"through". Consider the typical (American?) English phrase "open Mon
thru Fri, 9 to 5": Fri is included, but 5 is excluded.

So in other other words, the change from `stride(from: 1, to: 2, by: 0.1)` to `1.stride(to: 2, by: 0.1)` is the source of the problem and should be reversed.

(Sorry, pet peeve of mine.)

···

--
Brent Royal-Gordon
Architechies