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