[swift-evolution] [Proposal] Conventionalizing stride semantics

Xiaodi Wu xiaodi.wu at gmail.com
Tue Mar 1 02:54:46 CST 2016


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 at swift.org> wrote:
>
> On Feb 29, 2016, at 5:03 PM, Joe Groff <jgroff at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>


More information about the swift-evolution mailing list