[swift-evolution] Feature proposal: Range operator with step

Howard Lovatt howard.lovatt at gmail.com
Fri Apr 1 18:10:06 CDT 2016


No I would not be surprised by a limit based on Int provided that:

  1. It was articulated in the documentation
  2. The error occurred when the Range/stride was created
  3. The error message stated that there were too many steps and what the
step limit was

What I don't think would be acceptable was if:

  1. The error occurred part way through the iteration; not at creation time
  2. There was no error and the loop turned into an infinite loop silently

On Friday, 1 April 2016, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:

> All true. I call that Erica's solution because her proposal is where I
> first found it sketched out.
>
> I'm not convinced that Erica's solution is definitely the right answer
> because:
>
> (a) Use of an iteration counter of type Int to stride through Doubles is
> an implementation detail which is not an obviously correct choice; users
> might find it surprising that how many steps they get for StrideTo<Double>
> is constrained by Int.max
>
> (b) I'm not completely certain that there is no use case for a loop with
> more than Int.max steps so long as you break before the end, so I'm not
> completely certain that an error right off the bat is the most ideal
> behavior; for example, someone may wish to increment by a user-supplied
> epsilon from one user-supplied value to another but break after a certain
> amount of time has elapsed
>
> (c) I agree with you that it's Swiftier to do nothing than to start
> returning approximately correct values, but in a scenario such as `for _ in
> stride(from: 0, to: DBL_MAX, by: someAbsurdlySmallValue) { }` it may not
> matter (I cannot imagine a use case for this ridiculous loop, but for the
> sake of argument here let's take it); one alternative solution someone
> might propose, for example, would be to fall back to the old
> error-accumulating algorithm after the iteration counter has reached its
> max possible value
>
> So I guess the feedback I'm interested in is:
>
> - Would you be surprised to find that Stride<Double> may become
> constrained by an upper limit in the number of steps?
>
> - If not, would it irk you that such a limit is based on the size of a
> totally unrelated numeric type (namely, Int) which is an implementation
> detail? Would you prefer that the limit be something related to the nature
> of the type itself (for example, a maximum number of steps for
> StrideTo<Double> that reflects the maximum exactly representable integer in
> a Double)?
>
> - If there is to be an upper limit on steps, would you prefer an error
> when the Stride is being initialized or when the iteration counter
> overflows?
>
> - Would you rather instead be able to stride indefinitely, as is currently
> the case in Swift 2, accepting that error will start accumulating at some
> point?
>
> On Thu, Mar 31, 2016 at 4:33 PM Howard Lovatt via swift-evolution <
> swift-evolution at swift.org
> <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>> wrote:
>
>> If you define a range as range[i] = first + i * stride where i is an Int
>> then this generates an error when there are more than Int_Max steps, see
>> code previously posted. The error is generated when the range is formed,
>> which is ideal since an error part way along an iteration or a never ending
>> iteration would be difficult to track down.
>>
>> On Friday, 1 April 2016, Stephen Canon via swift-evolution <
>> swift-evolution at swift.org
>> <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>> wrote:
>>
>>>
>>> > On Mar 31, 2016, at 11:16 AM, Rainer Brockerhoff via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>> >
>>> > On 3/31/16 15:06, Dave Abrahams via swift-evolution wrote:
>>> >>
>>> >> on Thu Mar 31 2016, Xiaodi Wu <xiaodi.wu-AT-gmail.com> wrote:
>>> >>
>>> >>> Thoughts on an edge case: so long as it's possible to use
>>> >>> `stride(from:to:by:)` with Double, we'll need to figure out what
>>> >>> happens when you have `stride(from: 0.0, to: DBL_MAX, by: DBL_MIN)`.
>>> >>> Bounds may be unknown at compile time, obviously.
>>> >>>
>>> >>> Currently (this is by reasoning through the code, not actually
>>> >>> observing it run), `for i in stride(from: 0.0, to: DBL_MAX, by:
>>> >>> DBL_MIN) { }` degenerates into an infinite loop once you reach
>>> >>> sufficient large values such that `current + stride == current`,
>>> which
>>> >>> for a stride of DBL_MIN should happen pretty quickly.
>>> >>>
>>> >>> In Erica's proposed floating point Stride, an Int is used to count
>>> >>> iterations (and iterations need to be counted in order to avoid
>>> >>> accumulating error). Thus, one must break from `for i in stride(from:
>>> >>> 0.0, to: DBL_MAX, by: DBL_MIN) { }` before the iteration counter
>>> >>> overflows or it will trap. IMO, trapping at some point is fine, but I
>>> >>> think a limit of Int.max iterations might be rather arbitrary for a
>>> >>> StrideTo<Double> (or whatever it will be named) and I'm not sure how
>>> >>> one can justify why the behavior of StrideTo<Double> would change
>>> from
>>> >>> machine to machine based on the size of Int.
>>> >>>
>>> >>> I've been waffling between using an Int counter as Erica does or a
>>> >>> counter of type Strideable.Stride in `StrideTo<Strideable where
>>> >>> Strideable.Stride : FloatingPoint>`. In the latter alternative, no
>>> >>> trapping occurs, but error begins to accumulate when the iteration
>>> >>> counter is too large to represent integers exactly (e.g., 2^53 for
>>> >>> Double). In that case, `for i in stride(from: 0.0, to: DBL_MAX, by:
>>> >>> DBL_MIN) { }` degenerates into an infinite loop eventually (once
>>> >>> `iterationCount + 1.0 == iterationCount`) and never traps, which I'm
>>> >>> not sure I like, but a limit of 2^53 iterations bears at least a
>>> >>> rational connection to Double and is known at compile time
>>> independent
>>> >>> of the supplied bounds. We could alternatively return nil on reaching
>>> >>> 2^53 iterations, trap, etc.
>>> >>>
>>> >>> Comments?
>>> >>
>>> >> I think I want to hear Steve Canon's input on this one.  I defer to
>>> him
>>> >> on most things numeric.
>>> >
>>> > In particular, should Steve confirm that the IEEE754 Decimal128 format
>>> > is being worked on, and if simple decimal constants like those in
>>> >       `for i in stride(from: 0.0, to: DBL_MAX, by: DBL_MIN) { }`
>>> > will be type-inferred as Decimal128, all that would "just work".
>>>
>>> Decimal is something that I would like to see happen.  However, I would
>>> not expect any such proposal to result in that loop being type inferred to
>>> Decimal, since the to: and by: values are explicitly (binary
>>> floating-point) Doubles.
>>>
>>> I also don’t think that such a loop is particularly useful.  For
>>> floating-point types, something like stride(from: T, to: T, steps: Int)
>>> seems safer and more workable to me (this is just my immediate reaction, I
>>> haven’t thought this through in detail, and am likely to change my mind if
>>> someone makes a good case).
>>>
>>> – Steve
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>
>>
>> --
>> -- Howard.
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> <javascript:_e(%7B%7D,'cvml','swift-evolution at swift.org');>
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>

-- 
-- Howard.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160402/7438015b/attachment.html>


More information about the swift-evolution mailing list