[swift-evolution] Feature proposal: Range operator with step
Thorsten Seitz
tseitz42 at icloud.com
Sat Apr 2 12:43:39 CDT 2016
> Am 01.04.2016 um 00:23 schrieb Xiaodi Wu via swift-evolution <swift-evolution at swift.org>:
>
> 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
Couldn’t we use a BigInt internally (if necessary)? This would make this problem disappear.
-Thorsten
> (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 <mailto: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 <mailto: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 <http://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 <https://lists.swift.org/mailman/listinfo/swift-evolution>
>
>
> --
> -- Howard.
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160402/59f77a47/attachment.html>
More information about the swift-evolution
mailing list