>>      calendar.using(.Day).stride(from: startDate, to: endDate, by: 1)
> 
>> The `start` and `end` parameters could be grouped together into a
>> single parameter to match `stride(over:by:)`, but you can't put the
>> calendar or the unit into the stride—without them, there is no
>> coherent way to calculate the distance between two dates.
>> 
>> So if some types need a strider, and will need to have the method
>> structured as `strider.stride(something:by:)`, it seems like the free
>> function version for types which *don't* need a strider ought to be
>> `stride(something:by:)`. The `something.striding(by:)` design can't be
>> easily adapted to this situation.
> 
>  calendar[startDate..<endDate].striding(by: .Day)
> 
> ?

Actually, it would need to be something like `calendar[startDate..<endDate, 
unit: .Day].striding(by: 1)`, because NSCalendarUnit is not itself a stride, it 
is the *unit* of the stride. But even that doesn't quite help.

Here's the issue.

Take a look at today's Strideable. Stripped of comments and irrelevant 
annotations, you have:

        protocol Strideable : Comparable {
            associatedtype Stride : SignedNumberType
            func distanceTo(other: Self) -> Stride
            func advancedBy(n: Stride) -> Self
        }

The problem is that there's no good way to get the calendar and unit into these 
methods. If you package them inside `Stride`, then `distanceTo` has no idea 
what calendar or unit it's supposed to return. If you package them inside 
`Self`, then `distanceTo` has two calendars and two units, and they might not 
agree (and the type system can't catch this issue). 

To fix this, you need to have a single instance which performs the calculation. 
For simple Strideable types, this instance would not do very much, but for 
dates, it would factor in the calendar and unit.

For example, suppose you modify `Strideable` so that, instead of applying to 
the values participating in the striding, it applies to the instance containing 
those values:

        public protocol Strideable {
                associatedtype Element: Comparable
                associatedtype Stride: SignedNumber
                
                var start: Value
                var end: Value
                
                public func distance(from earlier: Element, to later: Element) 
-> Stride
                public func advance(element: Element, by stride: Stride) -> 
Element
        }

Now, with some *really* aggressive conditional conformance work, you could do 
this:

        extension Range: Strideable where Element == Int {
                typealias Stride = Int
                
                public func distance(from earlier: Int, to later: Int) -> Int {
                        return later - earlier
                }
                public func advance(element: Int, by stride: Int) {
                        return value + stride
                }
        }
        extension Range: Strideable where Element == Double {
                // Ignoring the accumulation issue.
                typealias Stride = Double
                
                public func distance(from earlier: Int, to later: Int) -> Int {
                        return later - earlier
                }
                public func advance(element: Double, by stride: Double) {
                        return element + stride
                }
        }
        // etc., for each type you care about.
        // 
        // This could be automated by creating a `RangeStrideable` protocol 
similar to the old Strideable,
        // and having types with this automatic behavior conform to it. You 
could then have a single:
        //      extension Range: Strideable where Element: RangeStrideable

It would not be possible to stride directly over a range of NSDates, but you 
could have a "calendar range" which could be `Strideable`, like so:

        struct NSCalendarRange {
                var start: NSDate
                var end: NSDate
                
                var calendar: NSCalendar
                var unit: NSCalendarUnit

                var options: NSCalendarOptions
                // This mainly contains rounding options. Interestingly, NSDate 
is like Double in that 
                // it accumulates errors through repeated addition. To fix 
that, I have discovered 
                // a truly marvelous design which this email is too small to 
contain.
        }
        extension NSCalendarRange: Strideable {
                typealias Value = NSDate
                typealias Stride = Int
                
                public func distance(from earlier: NSDate, to later: NSDate) -> 
Int {
                        let components = calendar.components(unit, from: 
earlier, to: later, options: options)
                        return components.value(forComponent: unit)
                }
                public func advance(value: NSDate, by stride: Int) -> NSDate {
                        return calendar.date(byAdding: unit, value: distance, 
to: value, options: options)!
                }
        }

So I guess `striding(by:)` can be adapted to date arithmetic, but only if we 
adjust our conception of what a stride is striding over.

-- 
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to